├── .gitignore
├── .vscode
└── launch.json
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── book.md
├── docgen.os
├── docs
├── ast.css
└── index.html
├── examples
├── backends
│ └── Классы
│ │ ├── ГенераторДокументацииДляПарсера.os
│ │ └── Компилятор.os
├── multiprocessing.ps1
├── param.json
├── plugins
│ └── Классы
│ │ ├── Визажист.os
│ │ ├── ДетекторВложенныхТернарныхОператоров.os
│ │ ├── ДетекторКонструкторовСтруктур.os
│ │ ├── ДетекторНеиспользуемыхПеременных.os
│ │ ├── ДетекторОшибочныхЗамыкающихКомментариев.os
│ │ ├── ДетекторПропущенныхТочекСЗапятой.os
│ │ ├── ДетекторФункцийБезВозвратаВКонце.os
│ │ ├── ЗаменаНеканоничныхКлючевыхСлов.os
│ │ ├── ПереименованиеПеременных.os
│ │ ├── ПлагинСПараметром.os
│ │ ├── ПодсчетКогнитивнойСложностиМетодов.os
│ │ ├── ПроверкаКаноничностиКлючевыхСлов.os
│ │ ├── РасстановкаПропущенныхТочекСЗапятой.os
│ │ └── РекурсивныйПодсчетСерверныхВызововВМодуляхФорм.os
├── test.os
├── test2.os
├── test3.os
├── test4.os
├── test5.os
├── test6.os
├── test7.os
├── test8.os
└── test9.os
├── packagedef
└── src
└── Классы
└── ПарсерВстроенногоЯзыка.os
/.gitignore:
--------------------------------------------------------------------------------
1 | *.txt
2 | sandbox
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Отладка 1Script",
9 | "type": "oscript",
10 | "request": "launch",
11 | "program": "${file}",
12 | "args": [],
13 | "cwd": "${workspaceRoot}",
14 | "runtimeExecutable": null,
15 | "debugPort": 2801,
16 | "protocol": "internal"
17 | }
18 | ]
19 | }
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Вы можете оказать следующую помощь проекту:
2 | * Сообщить об ошибке [тут](https://github.com/oscript-library/osparser/issues)
3 | * Предложить исправление ошибки любым удобным для вас способом
4 | * Поделиться идеями по улучшению конкретного кода или архитектуры в целом
5 | * Дополнить или улучшить документацию
6 |
7 | Формальностей на данный момент никаких нет. Просьба только сначала создавать [issue](https://github.com/oscript-library/osparser/issues), а pull request уже после обсуждения.
8 |
9 | Чтобы создать пулл реквест (запрос на вливание изменений из вашего форка в исходный репозиторий), нужно выполнить следующую последовательность шагов:
10 | * Установить [git](https://git-scm.com/)
11 | * Создать форк (копию) этого репозитория на [github](https://github.com/oscript-library/osparser) (кнопка справа вверху)
12 | * Клонировать этот форк себе на диск: `git clone https://github.com/<ваш_аккаунт>/osparser`
13 | * Внести правки
14 | * Зафиксировать изменения: `git add .` и `git commit -am "суть правки (#<номер_issue>)"`
15 | * Отправить изменения на github: `git push`
16 | * Открыть форк на github, перейти на вкладку "Pull requests" и создать новый
17 | * Подтвердить
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Tsukanov Alexander
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # osparser (oscript parser)
2 |
3 | ## Парсер встроенного языка платформы 1С:Предприятие 8
4 |
5 | Документация: [Книга Джедая](book.md)
6 |
7 | Установка: `opm install osparser`
8 |
9 | Самый короткий пример без плагинов:
10 |
11 | ```bsl
12 | #Использовать osparser
13 |
14 | Парсер = Новый ПарсерВстроенногоЯзыка;
15 | АСД = Парсер.Разобрать("x = x + 1;");
16 | Сообщить(АСД.Операторы.Количество());
17 | ```
18 |
19 | ## Содержание
20 |
21 | 1. [Введение](#введение)
22 | 2. [Структура репозитория](#структура-репозитория)
23 | 3. [Принцип работы](#принцип-работы)
24 | 4. [Благодарности](#благодарности)
25 |
26 | ## Введение
27 |
28 | Перед тем как разбираться с этим проектом, убедитесь что вы хорошо понимаете что такое AST и Visitor и что можно с их помощью делать.
29 | Это важно, так как данная разработка предоставляет именно эти возможности. Не больше не меньше.
30 | Вы можете писать проверки кода, компиляторы, интерпретаторы и любые другие вещи, которые можно реализовать путем обработки AST.
31 | Сколько информации содержит AST можно увидеть тут: https://oscript-library.github.io/osparser
32 |
33 | Общее представление можно получить в этой статье: [Зачем нужен AST](https://ps-group.github.io/compilers/ast)
34 |
35 | Данная разработка устроена похожим образом. С поправкой на то, что это реализация без ООП на языке 1С.
36 | После ознакомления со статьей можно сразу посмотреть [Принцип работы](#принцип-работы) внизу этой страницы.
37 |
38 | По сути это фронтенд компилятора, а вы можете к нему писать бакенды (плагины).
39 |
40 | Пример плагина проверяющего наличие возврата в конце функций: [ДетекторФункцийБезВозвратаВКонце](./examples/plugins/Классы/ДетекторФункцийБезВозвратаВКонце.os)
41 |
42 | Конкретно весь код проверки выглядит так (остальное там просто интерфейс плагина):
43 |
44 | ```bsl
45 | Процедура ПосетитьОбъявлениеМетода(ОбъявлениеМетода) Экспорт
46 | Перем КоличествоОператоров;
47 | Если ОбъявлениеМетода.Сигнатура.Тип <> Типы.ОбъявлениеСигнатурыФункции Тогда
48 | Возврат;
49 | КонецЕсли;
50 | КоличествоОператоров = ОбъявлениеМетода.Операторы.Количество();
51 | Если КоличествоОператоров = 0 Или ОбъявлениеМетода.Операторы[КоличествоОператоров - 1].Тип <> Типы.ОператорВозврат Тогда
52 | Текст = СтрШаблон("Последней инструкцией функции `%1()` должен быть `Возврат`""", ОбъявлениеМетода.Сигнатура.Имя);
53 | Ошибка(Текст, ОбъявлениеМетода.Конец);
54 | КонецЕсли;
55 | КонецПроцедуры
56 | ```
57 |
58 | Эта процедура вызывается визитером (Visitor) во время обхода AST для каждой встреченной процедуры или функции.
59 | Суть реализации проверки: Сначала проверяется что это функция. Затем берется количество операторов в теле функции.
60 | Если 0 или последний оператор не `Возврат`, то регистрируется ошибка.
61 |
62 | Пример плагина средней сложности: [ДетекторНеиспользуемыхПеременных](./examples/plugins/Классы/ДетекторНеиспользуемыхПеременных.os)
63 | (этот код находит неиспользуемые переменные и параметры)
64 |
65 | Пример сложного бакенда: [Компилятор](./examples/backends/Классы/Компилятор.os)
66 | (это генератор байткода, который работает идентично платформенному)
67 |
68 | Пример на OneScript, демонстрирующий прогон проверок исходного кода: [test.os](./examples/test.os)
69 |
70 | Пример на OneScript, демонстрирующий автоматическое исправление исходного кода: [test8.os](./examples/test8.os)
71 |
72 | Пример выгрузки ошибок в [SonarQube](https://www.sonarqube.org/): [test7.os](./examples/test7.os)
73 |
74 | ## Структура репозитория
75 |
76 | * /docs - файлы веб-страницы проекта
77 | * /examples - примеры
78 | * /src - исходники парсера
79 | * /docgen.os - скрипт, генерирующий документацию в папке /docs
80 |
81 | ## Принцип работы
82 |
83 | Парсер разбирает переданный ему исходный код и возвращает модель этого кода в виде [абстрактного синтаксического дерева](https://ru.wikipedia.org/wiki/%D0%90%D0%B1%D1%81%D1%82%D1%80%D0%B0%D0%BA%D1%82%D0%BD%D0%BE%D0%B5_%D1%81%D0%B8%D0%BD%D1%82%D0%B0%D0%BA%D1%81%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE). Узлы этого дерева соответствуют синтаксическим конструкциям и операторам языка. Например, конструкция `Пока <условие> Цикл <тело> КонецЦикла` представлена в дереве узлами типа `ОператорПока`, в которых условие представлено в подчиненном узле-выражении `Выражение`, а тело хранится в массиве узлов-операторов `Операторы`. Данных в дереве достаточно для полного восстановления по нему исходного кода вместе с комментариями, за исключением некоторых деталей форматирования. Порядок и подчиненность узлов в дереве в точности соответствует исходному коду. Описание узлов и элементов дерева вы можете найти на веб-странице проекта: [https://oscript-library.github.io/osparser](https://oscript-library.github.io/osparser)
84 |
85 | После формирования дерева запускается общий механизм обхода (шаблон проектирования Visitor), который при посещении узла вызывает обработчики подписанных на этот узел плагинов. Полезная (прикладная) работа выполняется именно плагином. Это может быть сбор статистики, поиск ошибок, анализ цикломатической сложности, построение документации по коду и т.д. и т.п. Кроме того, плагин может выполнить модификацию исходного кода путем регистрации замен фрагментов текста в исходнике (например, в целях исправления ошибок или форматирования).
86 |
87 | Состояние плагина (в переменных модуля) сохраняется между вызовами до самого конца обхода дерева, а подписки на каждый узел возможны две: перед обходом узла и после обхода. Это существенно упрощает реализацию многих алгоритмов анализа. Плюс к этому, некоторую информацию предоставляет сам механизм обхода. Например, плагинам доступна статистика по родительским узлам (количество каждого вида).
88 |
89 | ## Благодарности
90 |
91 | Спасибо всем кто так или иначе повлиял на этот проект.
92 |
--------------------------------------------------------------------------------
/book.md:
--------------------------------------------------------------------------------
1 | # Книга Джедая
2 |
3 | May the Force be with you.
4 |
5 | ## Содержание
6 |
7 | - [Парсер](#парсер)
8 | - [Общие сведения](#общие-сведения)
9 | - [Программный интерфейс парсера](#программный-интерфейс-парсера)
10 | - [Основные методы](#основные-методы)
11 | - [`Пуск`](#пускисходник-плагины-параметры-окружение-массив)
12 | - [`Разобрать`](#разобратьисходник-окружение-структура)
13 | - [`Подключить`](#подключитьплагины)
14 | - [`Посетить`](#посетитьмодуль-параметры)
15 | - [`Токенизировать`](#токенизироватьисходник-таблицазначений)
16 | - [`ВыполнитьЗамены`](#выполнитьзамены-строка)
17 | - [`Очистить`](#очистить)
18 | - [Дополнительные методы](#дополнительные-методы)
19 | - [`Узлы`](#узлы-структура)
20 | - [`КлючевыеСлова`](#ключевыеслова-структура)
21 | - [`Токены`](#токены-структура)
22 | - [`Типы`](#типы-структура)
23 | - [`Директивы`](#директивы-структура)
24 | - [`Аннотации`](#аннотации-структура)
25 | - [`СимволыПрепроцессора`](#символыпрепроцессора-структура)
26 | - [`Стек`](#стек-массив)
27 | - [`Счетчики`](#счетчики-соответствие)
28 | - [`Исходник`](#исходник-строка)
29 | - [`ТаблицаТокенов`](#таблицатокенов-таблицазначений)
30 | - [`ТаблицаОшибок`](#таблицаошибок-таблицазначений)
31 | - [`ТаблицаЗамен`](#таблицазамен-таблицазначений)
32 | - [Продвинутые методы](#продвинутые-методы)
33 | - [`Окружение`](#окружениевнешнееокружение-структура)
34 | - [`ЭлементОкружения`](#элементокруженияимя-объявление-структура)
35 | - [`ОбъявлениеГлобальногоОбъекта`](#объявлениеглобальногообъектаимя-доступность-структура)
36 | - [`ОбъявлениеГлобальногоМетода`](#объявлениеглобальногометодаимя-возвращаетзначение-параметры-доступность-структура)
37 | - [`Доступность`](#доступностьклиент-внешнеесоединение-мобильноеприложение-мобильныйклиент-мобильныйсервер-сервер-толстыйклиент-тонкийклиент-вебклиент-интеграция-структура)
38 | - [Служебные методы](#служебные-методы)
39 | - [`УстановитьИсходник`](#установитьисходникисходник)
40 | - [`УстановитьТаблицуТокенов`](#установитьтаблицутокеновтаблицатокенов)
41 | - [`УстановитьТаблицуОшибок`](#установитьтаблицуошибоктаблицаошибок)
42 | - [Параметры](#параметры)
43 | - [`СтрогийРежим`](#строгийрежим-булево)
44 | - [Плагины](#плагины)
45 | - [Программный интерфейс плагина](#программный-интерфейс-плагина)
46 | - [`Открыть`](#открытьпарсер-параметры)
47 | - [`Закрыть`](#закрыть-произвольный)
48 | - [`Подписки`](#подписки-массив)
49 | - [Минимальный шаблон плагина](#минимальный-шаблон-плагина)
50 | - [Полный шаблон плагина](#полный-шаблон-плагина)
51 | - [Бакенды](#бакенды)
52 | - [Программный интерфейс бакенда](#программный-интерфейс-бакенда)
53 | - [`Посетить`](#посетитьпарсер-асд-произвольный)
54 |
55 | ## Парсер
56 |
57 | ### Общие сведения
58 |
59 | Парсер полностью реализован в виде одного класса `ПарсерВстроенногоЯзыка`, который предоставляет три механизма:
60 |
61 | - Разбор исходного кода на встроенном языке платформы 1С:Предприятие 8.3 с результатом в виде AST.
62 | - Встроенный механизм обхода AST для плагинов.
63 | - Встроенный механизм замен участков текста в исходнике.
64 |
65 | Создать экземпляр парсера можно следующим образом:
66 |
67 | ```bsl
68 | #Использовать osparser
69 |
70 | Парсер = Новый ПарсерВстроенногоЯзыка;
71 | ```
72 |
73 | Типичный сценарий работы с плагинами, которые регистрируют ошибки:
74 |
75 | ```bsl
76 | Плагины = Новый Массив;
77 | Плагины.Добавить(Новый ДетекторНеиспользуемыхПеременных);
78 | Плагины.Добавить(Новый ДетекторПропущенныхТочекСЗапятой);
79 | Парсер.Пуск(Исходник, Плагины);
80 | Для Каждого Ошибка Из Парсер.ТаблицаОшибок() Цикл
81 | Сообщить(СтрШаблон("%1 [стр: %2; кол: %3]", Ошибка.Текст, Ошибка.НомерСтрокиНачала, Ошибка.НомерКолонкиНачала))
82 | КонецЦикла;
83 | ```
84 |
85 | Типичный сценарий работы с плагинами, которые генерируют текст:
86 |
87 | ```bsl
88 | Результаты = ПарсерВстроенногоЯзыка.Пуск(Исходник, Плагин);
89 | Сообщить(СтрСоединить(Результаты));
90 | ```
91 |
92 | Типичный сценарий работы с плагинами, которые регистрируют замены:
93 |
94 | ```bsl
95 | Плагины = Новый Массив;
96 | Плагины.Добавить(Новый РасстановкаПропущенныхТочекСЗапятой);
97 | Плагины.Добавить(Новый ЗаменаНеканоничныхКлючевыхСлов);
98 | Парсер.Пуск(Исходник, Плагины);
99 | ИсправленныйИсходник = Парсер.ВыполнитьЗамены();
100 | Сообщить(ИсправленныйИсходник);
101 | ```
102 |
103 | Типичный сценарий работы с бакендом:
104 |
105 | ```bsl
106 | АСД = Парсер.Разобрать(Исходник);
107 | Текст = Бакенд.Посетить(Парсер, АСД);
108 | Сообщить(Текст);
109 | ```
110 |
111 | ### Программный интерфейс парсера
112 |
113 | #### Основные методы
114 |
115 | Данные методы составляют основной программный интерфейс для внешней по отношению к парсеру системы. Позволяют выполнить разбор исходного кода, обход AST с плагинами и выполнить модификацию исходного кода по сгенерированным плагинами заменам.
116 |
117 | Чаще всего используется метод `Пуск()`, который заключает в себе типовую последовательность вызовов `Разобрать()`, `Подключить()`, `Посетить()` и `Очистить()`.
118 | Рабочие примеры использования данных методов смотрите в скриптах в папке [/examples](/examples)
119 |
120 | ##### `Пуск(Исходник, Плагины, Параметры, Окружение): Массив`
121 |
122 | *Выполняет разбор исходника в указанном окружении, обход AST с указанными плагинами и их параметрами и возвращает результаты (обычно текстовые) работы плагинов.*
123 |
124 | Параметры:
125 |
126 | - `Исходник: Строка` -- исходный код для анализа.
127 | - `Плагины: Массив или Объект` -- плагины, с которыми будет выполняться обход AST.
128 | - `Параметры: Соответствие (необязательный)` -- параметры плагинов.
129 | - `Окружение: Структура (необязательный)` -- окружение (контекст), в котором будет выполняться разбор исходника.
130 |
131 | Пример с одним плагином:
132 |
133 | ```bsl
134 | Исходник = ЧтениеТекста.Прочитать();
135 |
136 | Парсер = Новый ПарсерВстроенногоЯзыка;
137 |
138 | Плагин = Новый Плагин;
139 |
140 | Парсер.Пуск(Исходник, Плагин); // тут выполняется синтаксический анализ и обход АСД с плагинами
141 |
142 | Для Каждого Ошибка Из Парсер.ТаблицаОшибок() Цикл
143 | Сообщить(Ошибка.Текст); // вывод найденных парсером и плагином ошибок
144 | КонецЦикла;
145 | ```
146 |
147 | Пример с набором плагинов:
148 |
149 | ```bsl
150 | Исходник = ЧтениеТекста.Прочитать();
151 |
152 | Парсер = Новый ПарсерВстроенногоЯзыка;
153 |
154 | Плагины = Новый Массив;
155 | Плагины.Добавить(Новый Плагин1);
156 | Плагины.Добавить(Новый Плагин2);
157 |
158 | Парсер.Пуск(Исходник, Плагины); // тут выполняется синтаксический анализ и обход АСД с плагинами
159 |
160 | Для Каждого Ошибка Из Парсер.ТаблицаОшибок() Цикл
161 | Сообщить(Ошибка.Текст); // вывод найденных парсером и плагинами ошибок
162 | КонецЦикла;
163 | ```
164 |
165 | Пример с параметрами плагинов:
166 |
167 | ```bsl
168 | Исходник = ЧтениеТекста.Прочитать();
169 |
170 | Парсер = Новый ПарсерВстроенногоЯзыка;
171 | Плагин1 = Новый Плагин1;
172 | Плагин2 = Новый Плагин2;
173 |
174 | Плагины = Новый Массив;
175 | Плагины.Добавить(Плагин1);
176 | Плагины.Добавить(Плагин2);
177 |
178 | Параметры = Новый Соответствие; // у каждого плагина своя структура параметров
179 | Параметры[Плагин1] = Новый Структура("Параметр1, Параметр2", 1, 2);
180 | Параметры[Плагин2] = Новый Структура("Параметр1, Параметр2", 3, 4)
181 |
182 | Парсер.Пуск(Исходник, Плагины, Параметры); // тут выполняется синтаксический анализ и обход АСД с плагинами
183 |
184 | Для Каждого Ошибка Из Парсер.ТаблицаОшибок() Цикл
185 | Сообщить(Ошибка.Текст); // вывод найденных парсером и плагинами ошибок
186 | КонецЦикла;
187 | ```
188 |
189 | Пример с окружением:
190 |
191 | ```bsl
192 | Исходник = ЧтениеТекста.Прочитать();
193 |
194 | Парсер = Новый ПарсерВстроенногоЯзыка;
195 | Узлы = Парсер.Узлы();
196 |
197 | Плагин = Новый Плагин);
198 |
199 | Окружение = Парсер.Окружение();
200 |
201 | // например, контекст формы:
202 | Элемент = Узлы.ЭлементОкружения.Добавить();
203 | Элемент.Имя = "ЭтаФорма";
204 | ПроизвольноеОкружение.Переменные.Вставить("ЭтаФорма", Элемент);
205 | // ...
206 | Элемент = Узлы.ЭлементОкружения.Добавить();
207 | Элемент.Имя = "РеквизитФормыВЗначение";
208 | ПроизвольноеОкружение.Методы.Вставить("РеквизитФормыВЗначение", Элемент);
209 | // ...
210 |
211 | Парсер.Пуск(Исходник, Плагины,, Окружение);
212 |
213 | Для Каждого Ошибка Из Парсер.ТаблицаОшибок() Цикл
214 | Сообщить(Ошибка.Текст); // вывод найденных парсером и плагином ошибок
215 | КонецЦикла;
216 | ```
217 |
218 | ##### `Разобрать(Исходник, Окружение): Структура`
219 |
220 | *Выполняет разбор исходника в указанном окружении и возвращает AST.*
221 |
222 | Параметры:
223 |
224 | - `Исходник: Строка` -- исходный код для анализа.
225 | - `Окружение: Структура (необязательный)` -- окружение (контекст), в котором будет выполняться разбор исходника.
226 |
227 | Пример:
228 |
229 | ```bsl
230 | Исходник = ЧтениеТекста.Прочитать();
231 |
232 | Парсер = Новый ПарсерВстроенногоЯзыка;
233 | Узлы = Парсер.Узлы();
234 |
235 | Окружение = Парсер.Окружение();
236 |
237 | // например, контекст формы:
238 | Элемент = Узлы.ЭлементОкружения.Добавить();
239 | Элемент.Имя = "ЭтаФорма";
240 | ПроизвольноеОкружение.Переменные.Вставить("ЭтаФорма", Элемент);
241 | // ...
242 | Элемент = Узлы.ЭлементОкружения.Добавить();
243 | Элемент.Имя = "РеквизитФормыВЗначение";
244 | ПроизвольноеОкружение.Методы.Вставить("РеквизитФормыВЗначение", Элемент);
245 | // ...
246 |
247 | АСД = Парсер.Разобрать(Исходник, Окружение);
248 |
249 | Сообщить(АСД.Объявления.Количество());
250 | ```
251 |
252 | ##### `Подключить(Плагины)`
253 |
254 | *Подключает плагины к парсеру.*
255 |
256 | Параметры:
257 |
258 | - `Плагины: Массив или Объект` -- плагины для подключения к парсеру.
259 |
260 | Пример:
261 |
262 | ```bsl
263 | Парсер = Новый ПарсерВстроенногоЯзыка;
264 | Плагин = Новый Плагин;
265 |
266 | АСД = Парсер.Разобрать(Исходник); // тут выполняется синтаксический анализ
267 | Парсер.Подключить(Плагин); // тут плагин подключается к парсеру
268 | Парсер.Посетить(АСД); // тут выполняется обход АСД с вызовом подписок плагина
269 |
270 | Для Каждого Ошибка Из Парсер.ТаблицаОшибок() Цикл
271 | Сообщить(Ошибка.Текст); // вывод найденных парсером и плагином ошибок
272 | КонецЦикла;
273 | ```
274 |
275 | ##### `Посетить(Модуль, Параметры)`
276 |
277 | *Выполняет обход AST с подключенными плагинами и указанными параметрами плагинов.*
278 |
279 | Параметры:
280 |
281 | - `Модуль: Структура` -- АСД для обхода.
282 | - `Параметры: Соответствие (необязательный)` -- параметры плагинов, с которыми нужно выполнить обход.
283 |
284 | Пример:
285 |
286 | ```bsl
287 | Исходник = ЧтениеТекста.Прочитать();
288 |
289 | Парсер = Новый ПарсерВстроенногоЯзыка;
290 | Плагин = Новый Плагин;
291 |
292 | АСД = Парсер.Разобрать(Исходник); // тут выполняется синтаксический анализ
293 | Парсер.Подключить(Плагин); // тут плагин подключается к парсеру
294 | Парсер.Посетить(АСД); // тут выполняется обход АСД с вызовом подписок плагина
295 |
296 | Для Каждого Ошибка Из Парсер.ТаблицаОшибок() Цикл
297 | Сообщить(Ошибка.Текст); // вывод найденных парсером и плагином ошибок
298 | КонецЦикла;
299 | ```
300 |
301 | ##### `Токенизировать(Исходник): ТаблицаЗначений`
302 |
303 | *Выполняет лексический анализ и возвращает таблицу токенов.*
304 |
305 | Параметры:
306 |
307 | - `Исходник: Строка` -- исходный код для анализа.
308 |
309 | Пример:
310 |
311 | ```bsl
312 | Исходник = ЧтениеТекста.Прочитать();
313 |
314 | Парсер = Новый ПарсерВстроенногоЯзыка;
315 |
316 | ТаблицаТокенов = Парсер.Токенизировать(Исходник); // тут выполняется лексический анализ
317 |
318 | Для Каждого ДанныеТокена Из ТаблицаТокенов Цикл
319 | Сообщить(ДанныеТокена.Токен); // вывод всех токенов
320 | КонецЦикла;
321 | ```
322 |
323 | ##### `ВыполнитьЗамены(): Строка`
324 |
325 | *Возвращает исходник после выполнения на нем замен участков текста.*
326 |
327 | Пример:
328 |
329 | ```bsl
330 | Исходник = ЧтениеТекста.Прочитать();
331 |
332 | Парсер = Новый ПарсерВстроенногоЯзыка;
333 | Плагин = Новый Плагин;
334 |
335 | Парсер.Пуск(Исходник, Плагин); // тут плагин наполняет таблицу замен
336 |
337 | ИсправленныйИсходник = Парсер.ВыполнитьЗамены(); // тут по таблице замен последовательно заменяются участки текста в исходнике.
338 |
339 | Сообщить(ИсправленныйИсходник)
340 | ```
341 |
342 | ##### `Очистить()`
343 |
344 | *Очищает в парсере таблицы токенов и узлов. Так освобождается память и парсер подготавливается к следующему разбору. Перед разбором таблицы улов не очищаются, т.к. могут быть наполнены извне при формировании внешнего окружения. Метод `Пуск()` уже включает этот вызов.*
345 |
346 | Пример:
347 |
348 | ```bsl
349 | ПарсерВстроенногоЯзыка = Новый ПарсерВстроенногоЯзыка;
350 | АСД = ПарсерВстроенногоЯзыка.Разобрать(Исходник);
351 | Сайт = ГенераторДокументации.Посетить(ПарсерВстроенногоЯзыка, АСД);
352 | ПарсерВстроенногоЯзыка.Очистить();
353 | ```
354 |
355 | #### Дополнительные методы
356 |
357 | Данные методы в основном используются в плагинах и вызываются один раз в процедуре Открыть(). Методы предоставляют различную вспомогательную информацию и таблицы, в которых плагины могут регистрировать ошибки и замены.
358 |
359 | Примеры использования данных методов смотрите в плагинах в папке /plugins
360 |
361 | ##### `Узлы(): Структура`
362 |
363 | *Возвращает структуру таблиц, хранящих узлы. На каждый тип узла отдельная таблица в одноименном свойстве структуры.*
364 |
365 | Пример (фрагмент кода плагина):
366 |
367 | ```bsl
368 | Перем Узлы;
369 |
370 | Процедура Открыть(Парсер, Параметры) Экспорт
371 | Узлы = Парсер.Узлы();
372 | КонецПроцедуры
373 |
374 | Функция Закрыть() Экспорт
375 | Для Каждого Объявление Из Узлы.ОбъявлениеМетода Цикл
376 | Сообщить(Объявление.Сигнатура.Имя);
377 | КонецЦикла;
378 | Возврат Неопределено;
379 | КонецФункции
380 | ```
381 |
382 | ##### `КлючевыеСлова(): Структура`
383 |
384 | *Возвращает перечисление допустимых ключевых слов.*
385 |
386 | Пример (фрагмент кода плагина):
387 |
388 | ```bsl
389 | Перем ТаблицаТокенов, КлючевыеСлова;
390 |
391 | Процедура Открыть(Парсер, Параметры) Экспорт
392 | ТаблицаТокенов = Парсер.ТаблицаТокенов();
393 | КлючевыеСлова = Парсер.КлючевыеСлова();
394 | КонецПроцедуры
395 |
396 | Функция Закрыть() Экспорт
397 | Для Каждого ДанныеТокена Из ТаблицаТокенов Цикл
398 | Если ДанныеТокена.Токен = КлючевыеСлова.ИначеЕсли Тогда
399 | // ...
400 | КонецЕсли;
401 | КонецЦикла;
402 | Возврат Неопределено;
403 | КонецФункции
404 | ```
405 |
406 | ##### `Токены(): Структура`
407 |
408 | *Возвращает перечисление допустимых токенов.*
409 |
410 | Пример (фрагмент кода плагина):
411 |
412 | ```bsl
413 | Перем ТаблицаТокенов, Токены;
414 |
415 | Процедура Открыть(Парсер, Параметры) Экспорт
416 | ТаблицаТокенов = Парсер.ТаблицаТокенов();
417 | Токены = Парсер.Токены();
418 | КонецПроцедуры
419 |
420 | Функция Закрыть() Экспорт
421 | Для Каждого ДанныеТокена Из ТаблицаТокенов Цикл
422 | Если ДанныеТокена.Токен = Токены.ЗнакСложения Тогда
423 | // ...
424 | КонецЕсли;
425 | КонецЦикла;
426 | Возврат Неопределено;
427 | КонецФункции
428 | ```
429 |
430 | ##### `Типы(): Структура`
431 |
432 | *Возвращает перечисление допустимых типов узлов AST.*
433 |
434 | Пример (фрагмент кода плагина):
435 |
436 | ```bsl
437 | Перем Типы;
438 |
439 | Процедура Открыть(Парсер, Параметры) Экспорт
440 | Типы = Парсер.Типы();
441 | КонецПроцедуры
442 |
443 | Процедура ПосетитьОператоры(Операторы) Экспорт
444 | Для Каждого Оператор Из Операторы Цикл
445 | Если Оператор.Тип = Типы.ОператорПрисваивания Тогда
446 | // ...
447 | КонецЕсли;
448 | КонецЦикла;
449 | КонецПроцедуры
450 | ```
451 |
452 | ##### `Директивы(): Структура`
453 |
454 | *Возвращает перечисление допустимых директив.*
455 |
456 | Пример (фрагмент кода плагина):
457 |
458 | ```bsl
459 | Перем Директивы;
460 |
461 | Процедура Открыть(Парсер, Параметры) Экспорт
462 | Директивы = Парсер.Директивы();
463 | КонецПроцедуры
464 |
465 | Процедура ПосетитьОбъявлениеПеременнойМодуля(Объявление) Экспорт
466 | Если Объявление.Директивы.Количество() = 1
467 | И Объявление.Директивы[0].Директива = Директивы.НаКлиенте Тогда
468 | // ...
469 | КонецЕсли;
470 | КонецПроцедуры
471 | ```
472 |
473 | ##### `Аннотации(): Структура`
474 |
475 | *Возвращает перечисление допустимых аннотаций.*
476 |
477 | Пример (фрагмент кода плагина):
478 |
479 | ```bsl
480 | Перем Аннотации;
481 |
482 | Процедура Открыть(Парсер, Параметры) Экспорт
483 | Аннотации = Парсер.Аннотации();
484 | КонецПроцедуры
485 |
486 | Процедура ПосетитьОбъявлениеМетода(Объявление) Экспорт
487 | Аннотации = Объявление.Сигнатура.Аннотации;
488 | Если Аннотации.Количество() = 1
489 | И Аннотации[0].Аннотация = Аннотации.Вместо Тогда
490 | // ...
491 | КонецЕсли;
492 | КонецПроцедуры
493 | ```
494 |
495 | ##### `СимволыПрепроцессора(): Структура`
496 |
497 | *Возвращает перечисление допустимых символов препроцессора.*
498 |
499 | Пример (фрагмент кода плагина):
500 |
501 | ```bsl
502 | Перем СимволыПрепроцессора;
503 |
504 | Процедура Открыть(Парсер, Параметры) Экспорт
505 | СимволыПрепроцессора = Парсер.СимволыПрепроцессора();
506 | КонецПроцедуры
507 |
508 | Процедура ПосетитьВыражениеПрепроцессораСимвол(Выражение) Экспорт
509 | Если Выражение.Символ = СимволыПрепроцессора.ТолстыйКлиентОбычноеПриложение Тогда
510 | // ...
511 | КонецЕсли;
512 | КонецПроцедуры
513 | ```
514 |
515 | ##### `Стек(): Массив`
516 |
517 | *Возвращает стек узлов, который меняется во время обхода AST и позволяет получить текущие родительские узлы в подписках плагинов.*
518 |
519 | Пример (фрагмент кода плагина):
520 |
521 | ```bsl
522 | Перем Стек;
523 |
524 | Процедура Открыть(Парсер, Параметры) Экспорт
525 | Стек = Парсер.Стек();
526 | КонецПроцедуры
527 |
528 | Процедура ПосетитьВыражениеТернарное(Выражение) Экспорт
529 | Для Каждого Родитель Из Стек Цикл
530 | Если Родитель.Тип = Типы.ВыражениеТернарное Тогда
531 | Сообщить("Вложенный тернарный оператор");
532 | Прервать;
533 | КонецЕсли;
534 | КонецЦикла;
535 | КонецПроцедуры
536 | ```
537 |
538 | ##### `Счетчики(): Соответствие`
539 |
540 | *Возвращает счетчики родительских узлов, которые меняются во время обхода AST и позволяет получить количество текущих родительских узлов по типам в подписках плагинов.*
541 |
542 | Пример (фрагмент кода плагина):
543 |
544 | ```bsl
545 | Перем Счетчики;
546 |
547 | Процедура Открыть(Парсер, Параметры) Экспорт
548 | Счетчики = Парсер.Счетчики();
549 | КонецПроцедуры
550 |
551 | Процедура ПосетитьВыражениеТернарное(Выражение) Экспорт
552 | Если Счетчики.ВыражениеТернарное > 0 Тогда
553 | Сообщить("Вложенный тернарный оператор");
554 | КонецЕсли;
555 | КонецПроцедуры
556 | ```
557 |
558 | ##### `Исходник(): Строка`
559 |
560 | *Возвращает текущий исходник.*
561 |
562 | Пример (фрагмент кода плагина):
563 |
564 | ```bsl
565 | Перем Исходник, Токены, ТаблицаТокенов;
566 |
567 | Процедура Открыть(Парсер, Параметры) Экспорт
568 | Исходник = Парсер.Исходник();
569 | Токены = Парсер.Токены();
570 | ТаблицаТокенов = Парсер.ТаблицаТокенов();
571 | КонецПроцедуры
572 |
573 | Процедура ПосетитьОбъявлениеМетода(ОбъявлениеМетода) Экспорт
574 | ДанныеСледующегоТокена = ТаблицаТокенов[ОбъявлениеМетода.Конец.Индекс + 1];
575 | Если ДанныеСледующегоТокена.Токен = Токены.Комментарий Тогда
576 | Сообщить(Сред(Исходник, ДанныеСледующегоТокена.Позиция, ДанныеСледующегоТокена.Длина));
577 | КонецЕсли;
578 | КонецПроцедуры
579 | ```
580 |
581 | ##### `ТаблицаТокенов(): ТаблицаЗначений`
582 |
583 | *Возвращает текущую таблицу токенов, которая содержит подробную информацию о положении каждого токена в исходном коде. Каждый узел AST имеет поля `Начало` и `Конец`, которые хранят строки данной таблицы и представляют первый и последний токен узла. Например, получить номер первой строки узла можно так: `НомерСтроки = Узел.Начало.НомерСтроки`. Получить следующий за узлом токен можно так: `ДанныеСледующегоТокена = ТаблицаТокенов[Узел.Конец.Индекс + 1]`.*
584 |
585 | Колонки:
586 |
587 | - `Индекс: Число` -- индекс строки для удобства.
588 | - `Токен: Строка` -- токен из перечисления Токены.
589 | - `НомерСтроки: Число` -- номер строки, в которой находится токен.
590 | - `НомерКолонки: Число` -- номер колонки, в которой начинается токен.
591 | - `Позиция: Число` -- позиция в тексте, в которой начинается токен.
592 | - `Длина: Число` -- длина участка текста, который представляет токен.
593 |
594 | Пример (фрагмент кода плагина):
595 |
596 | ```bsl
597 | Перем Исходник, Токены, ТаблицаТокенов;
598 |
599 | Процедура Открыть(Парсер, Параметры) Экспорт
600 | Исходник = Парсер.Исходник();
601 | Токены = Парсер.Токены();
602 | ТаблицаТокенов = Парсер.ТаблицаТокенов();
603 | КонецПроцедуры
604 |
605 | Процедура ПосетитьОбъявлениеМетода(ОбъявлениеМетода) Экспорт
606 | ДанныеСледующегоТокена = ТаблицаТокенов[ОбъявлениеМетода.Конец.Индекс + 1];
607 | Если ДанныеСледующегоТокена.Токен = Токены.Комментарий Тогда
608 | Сообщить(Сред(Исходник, ДанныеСледующегоТокена.Позиция, ДанныеСледующегоТокена.Длина));
609 | КонецЕсли;
610 | КонецПроцедуры
611 | ```
612 |
613 | ##### `ТаблицаОшибок(): ТаблицаЗначений`
614 |
615 | *Возвращает текущую таблицу ошибок, в которой плагины могут регистрировать ошибки.*
616 |
617 | Колонки:
618 |
619 | - `Источник: Строка` -- имя плагина, который зарегистрировал ошибку.
620 | - `Текст: Строка` -- текст ошибки
621 | - `ПозицияНачала: Число` -- позиция в тексте, с которой начинается ошибочный участок.
622 | - `ПозицияКонца: Число` -- позиция в тексте, на которой заканчивается ошибочный участок.
623 | - `ЕстьЗамена: Булево` -- признак, что для ошибки есть замена (исправление) в таблице замен.
624 | - `Код: Число` -- код ошибки (для плагинов всегда 0).
625 | - `НомерСтрокиНачала: Число` -- номер строки, на которой начинается ошибочный участок.
626 | - `НомерКолонкиНачала: Число` -- номер колонки, с которой начинается ошибочный участок.
627 | - `НомерСтрокиКонца: Число` -- номер строки, на которой заканчивается ошибочный участок.
628 | - `НомерКолонкиКонца: Число` -- номер колонки, на которой заканчивается ошибочный участок.
629 | - `МинутНаИсправление: Число` -- примерная оценка затрат на исправление ошибки в минутах.
630 | - `Серьезность: Строка` -- произвольный текст на усмотрение разработчика.
631 | - `Приоритет: Число` -- произвольное число на усмотрение разработчика.
632 | - `Правило: Строка` -- произвольный текст на усмотрение разработчика.
633 | - `Тип: Строка` -- произвольный текст на усмотрение разработчика.
634 |
635 | Пример (фрагмент кода плагина):
636 |
637 | ```bsl
638 | Перем Исходник, Токены, ТаблицаТокенов;
639 |
640 | Процедура Открыть(Парсер, Параметры) Экспорт
641 | Исходник = Парсер.Исходник();
642 | Токены = Парсер.Токены();
643 | ТаблицаТокенов = Парсер.ТаблицаТокенов();
644 | КонецПроцедуры
645 |
646 | Процедура ПосетитьОбъявлениеМетода(ОбъявлениеМетода) Экспорт
647 |
648 | СледующийТокен = ТаблицаТокенов[ОбъявлениеМетода.Конец.Индекс + 1];
649 |
650 | Если СледующийТокен.Токен = Токены.Комментарий
651 | И СледующийТокен.НомерСтроки = ОбъявлениеМетода.Конец.НомерСтроки Тогда
652 |
653 | Комментарий = СокрП(Сред(Исходник, СледующийТокен.Позиция, СледующийТокен.Длина));
654 | ПравильныйКомментарий = СтрШаблон(" %1%2", ОбъявлениеМетода.Сигнатура.Имя, "()");
655 |
656 | Если Комментарий <> ПравильныйКомментарий Тогда
657 | Ошибка("Замыкающий комментарий неактуален", СледующийТокен);
658 | КонецЕсли;
659 |
660 | КонецЕсли;
661 |
662 | КонецПроцедуры
663 |
664 | Процедура Ошибка(Текст, ДанныеТокена)
665 | Ошибка = ТаблицаОшибок.Добавить();
666 | Ошибка.Источник = "ИмяЭтогоПлагина";
667 | Ошибка.Текст = Текст;
668 | Ошибка.ПозицияНачала = ДанныеТокена.Позиция;
669 | Ошибка.НомерСтрокиНачала = ДанныеТокена.НомерСтроки;
670 | Ошибка.НомерКолонкиНачала = ДанныеТокена.НомерКолонки;
671 | Ошибка.ПозицияКонца = ДанныеТокена.Позиция + ДанныеТокена.Длина;
672 | Ошибка.НомерСтрокиКонца = ДанныеТокена.НомерСтроки;
673 | Ошибка.НомерКолонкиКонца = ДанныеТокена.НомерКолонки + ДанныеТокена.Длина;
674 | КонецПроцедуры
675 | ```
676 |
677 | ##### `ТаблицаЗамен(): ТаблицаЗначений`
678 |
679 | *Возвращает текущую таблицу замен, в которой плагины могут регистрировать замены.*
680 |
681 | Колонки:
682 |
683 | - `Источник: Число` -- имя плагина, который зарегистрировал замену.
684 | - `Текст: Строка` -- фрагмент текста, на который нужно заменить указанный участок.
685 | - `Позиция: Число` -- позиция участка текста для замены.
686 | - `Длина: Число` -- длина участка текста для замены.
687 |
688 | Пример (фрагмент кода плагина):
689 |
690 | ```bsl
691 | Перем Исходник, Токены, ТаблицаТокенов, ТаблицаЗамен;
692 |
693 | Процедура Открыть(Парсер, Параметры) Экспорт
694 | Исходник = Парсер.Исходник();
695 | Токены = Парсер.Токены();
696 | ТаблицаТокенов = Парсер.ТаблицаТокенов();
697 | ТаблицаЗамен = Парсер.ТаблицаЗамен();
698 | КонецПроцедуры
699 |
700 | Процедура ПосетитьОбъявлениеМетода(ОбъявлениеМетода) Экспорт
701 |
702 | СледующийТокен = ТаблицаТокенов[ОбъявлениеМетода.Конец.Индекс + 1];
703 |
704 | Если СледующийТокен.Токен = Токены.Комментарий
705 | И СледующийТокен.НомерСтроки = ОбъявлениеМетода.Конец.НомерСтроки Тогда
706 |
707 | Комментарий = СокрП(Сред(Исходник, СледующийТокен.Позиция, СледующийТокен.Длина));
708 | ПравильныйКомментарий = СтрШаблон(" %1%2", ОбъявлениеМетода.Сигнатура.Имя, "()");
709 |
710 | Если Комментарий <> ПравильныйКомментарий Тогда
711 | Замена(ПравильныйКомментарий, СледующийТокен);
712 | КонецЕсли;
713 |
714 | КонецЕсли;
715 |
716 | КонецПроцедуры
717 |
718 | Процедура Замена(Текст, ДанныеТокена)
719 | НоваяЗамена = ТаблицаЗамен.Добавить();
720 | НоваяЗамена.Источник = "ИмяЭтогоПлагина";
721 | НоваяЗамена.Текст = Текст;
722 | НоваяЗамена.Позиция = ДанныеТокена.Позиция;
723 | НоваяЗамена.Длина = ДанныеТокена.Длина;
724 | КонецПроцедуры
725 | ```
726 |
727 | #### Продвинутые методы
728 |
729 | Данные методы позволяют сформировать окружение, в контексте которого будет выполняться разбор исходного кода.
730 |
731 | Пример формирования окружения смотрите в скрипте: [/examples/test5.os](/examples/test5.os)
732 |
733 | ##### `Окружение(ВнешнееОкружение): Структура`
734 |
735 | *Конструктор. Возвращает новое окружение.*
736 |
737 | Параметры:
738 |
739 | - `ВнешнееОкружение: Структура` -- внешнее окружение по отношению к создаваемому.
740 |
741 | ##### `Доступность(Клиент, ВнешнееСоединение, МобильноеПриложение, МобильныйКлиент, МобильныйСервер, Сервер, ТолстыйКлиент, ТонкийКлиент, ВебКлиент, Интеграция): Структура`
742 |
743 | *Конструктор. Возвращает новую структуру доступности.*
744 |
745 | Параметры:
746 |
747 | - `Клиент: Булево` -- признак доступности в одноименном контексте.
748 | - `ВнешнееСоединение: Булево` -- признак доступности в одноименном контексте.
749 | - `МобильноеПриложение: Булево` -- признак доступности в одноименном контексте.
750 | - `МобильныйКлиент: Булево` -- признак доступности в одноименном контексте.
751 | - `МобильныйСервер: Булево` -- признак доступности в одноименном контексте.
752 | - `Сервер: Булево` -- признак доступности в одноименном контексте.
753 | - `ТолстыйКлиент: Булево` -- признак доступности в одноименном контексте.
754 | - `ТонкийКлиент: Булево` -- признак доступности в одноименном контексте.
755 | - `ВебКлиент: Булево` -- признак доступности в одноименном контексте.
756 | - `Интеграция: Булево` -- признак доступности в одноименном контексте.
757 |
758 | #### Служебные методы
759 |
760 | Эти методы позволяют частично установить состояние парсера без выполнения разбора.
761 | Это необходимо при выполнении обхода кэшированных AST, чтобы плагинам была доступна соответствующая информация при инициализации.
762 |
763 | ##### `УстановитьИсходник(Исходник)`
764 |
765 | *Устанавливает исходник напрямую без выполнения анализа.*
766 |
767 | Параметры:
768 |
769 | - `Исходник: Строка` -- исходный код, который нужно установить в парсер.
770 |
771 | ##### `УстановитьТаблицуТокенов(ТаблицаТокенов)`
772 |
773 | *Устанавливает таблицу токенов напрямую без выполнения анализа.*
774 |
775 | Параметры:
776 |
777 | - `ТаблицаТокенов: ТаблицаЗначений` -- таблица токенов, которую нужно установить в парсер.
778 |
779 | ##### `УстановитьТаблицуОшибок(ТаблицаОшибок)`
780 |
781 | *Устанавливает таблицу ошибок напрямую без выполнения анализа.*
782 |
783 | Параметры:
784 |
785 | - `ТаблицаОшибок: ТаблицаЗначений` -- таблица ошибок, которую нужно установить в парсер.
786 |
787 | #### Параметры
788 |
789 | ##### `СтрогийРежим: Булево`
790 |
791 | *Определяет поведение парсера при возникновении ошибочных ситуаций.*
792 |
793 | ## Плагины
794 |
795 | С помощью плагинов можно решать много различных задач. В частности:
796 |
797 | - Поиск ошибок в исходном коде.
798 | - Проверка исходного кода на соответствие стандартам как внешним так и внутренним.
799 | - Проверка исходного кода на соответствие требованиям безопасности.
800 | - Проверка оформления исходного кода.
801 | - Модификация исходного кода путем замен/вставки/удаления фрагментов.
802 | - Форматирование исходного кода.
803 | - Генерация документации по исходному коду.
804 | - Подготовка исходного кода к публикации (генерация html).
805 | - Сбор статистики по исходному коду.
806 | - Выполнение автоматизированных переименований и переводов.
807 | - Перевод исходного кода на другие языки программирования.
808 | - Обфускация исходного кода.
809 | - Интерпретация и компиляция.
810 | - Продвинутый автоматизированный поиск по исходному коду.
811 |
812 | Плагин реализуется в виде одного класса.
813 |
814 | Для создания плагина нужно выполнить следующую последовательность шагов:
815 |
816 | 1. Создать новый класс.
817 | 2. В модуль класса скопировать шаблон с сайта: [osparser](https://oscript-library.github.io/osparser)
818 | 3. Заглянуть в метод **`Подписки()` в классе парсера** и выбрать те, которые нужны для решения задачи.
819 | Например, если нужно проверить все присваивания, то можно выбрать подписку "ПосетитьОператорПрисваивания".
820 | 4. Добавить подписку в массив в методе `Подписки()` будущего плагина. Это указание парсеру что плагин реализует такую процедуру.
821 | 5. Создать одноименную экспортную процедуру и написать в ней код, решающий задачу.
822 | 6. Удалить лишние переменные, которые были скопированы из шаблона, но в итоге не пригодились.
823 |
824 | ### Программный интерфейс плагина
825 |
826 | Интерфейс плагина состоит из трех постоянных методов и набора методов-подписок.
827 |
828 | Например, плагин может подписаться на посещение узла оператора присваивания. В этом случае он должен реализовать экспортный метод `ПосетитьОператорПрисваивания(ОператорПрисваивания)` и вернуть имя этого метода `ПосетитьОператорПрисваивания` в методе `Подписки()`
829 |
830 | #### `Открыть(Парсер, Параметры)`
831 |
832 | *Вызывается один раз перед обходом AST; позволяет выполнить необходимую инициализацию плагина.*
833 |
834 | Параметры:
835 |
836 | - `Парсер: Объект` -- экземпляр парсера, к кторому подключен плагин.
837 | - `Параметры: Структура` -- параметры плагина, переданные парсеру в методе `Пуск()` или `Посетить()`.
838 |
839 | #### `Закрыть(): Произвольный`
840 |
841 | *Вызывается один раз после обхода AST, позволяет плагину "подвести итоги" и вернуть результат своей работы. Некоторые плагины всю работу выполняют в данном методе.*
842 |
843 | #### `Подписки(): Массив`
844 |
845 | *Вызывается один раз при подключении плагинов. Возвращает список имен процедур-подписок, которые реализует данный плагин.*
846 |
847 | ### Минимальный шаблон плагина
848 |
849 | ```bsl
850 | Перем Результат;
851 |
852 | // Будет вызвана один раз перед обходом AST.
853 | // Тут можно получить необходимые перечисления и таблицы из парсера,
854 | // и выполнить инициализацию плагина с учетом полученных параметров.
855 | Процедура Открыть(Парсер, Параметры) Экспорт
856 | Результат = Новый Массив;
857 | КонецПроцедуры
858 |
859 | // Будет вызвана после полного обхода AST.
860 | // Возвращает текстовый результат работы плагина, если он есть.
861 | // Плагины, которые регистрируют ошибки и/или замены, могут вернуть Неопределено.
862 | Функция Закрыть() Экспорт
863 | Возврат СтрСоединить(Результат);
864 | КонецФункции
865 |
866 | // Возвращает список процедур-подписок, которые будут вызываться визитером.
867 | // Состав возможных подписок можно посмотреть в исходнике парсера в функции Подписки().
868 | // Имена большинства подписок образуются добавлением префикса Посетить/Покинуть к имени типа узла.
869 | // Справка по типам узлов находится по этому адресу: https://oscript-library.github.io/osparser/
870 | Функция Подписки() Экспорт
871 | Перем Подписки;
872 | Подписки = Новый Массив;
873 | Подписки.Добавить("ПосетитьОператорПрисваивания");
874 | Подписки.Добавить("ПокинутьОператорПрисваивания");
875 | Возврат Подписки;
876 | КонецФункции
877 |
878 | #Область РеализацияПодписок
879 |
880 | // Описание структуры узла `ОператорПрисваивания`: https://oscript-library.github.io/osparser/#ОператорПрисваивания
881 |
882 | // Данная процедура будет вызвана при посещении узла AST перед посещением подчиненных ему узлов.
883 | // Вызов подписок с префиксом `Посетить` отражает рекурсивный спуск визитера по AST.
884 | // Сначала вызывается подписка на родительский узел, потом на этот, потом на подчиненный и так далее.
885 | Процедура ПосетитьОператорПрисваивания(ОператорПрисваивания) Экспорт
886 | // ...
887 | КонецПроцедуры
888 |
889 | // Данная процедура будет вызвана при посещении узла AST после посещения подчиненных ему узлов.
890 | // Вызов подписок с префиксом `Покинуть` отражает рекурсивный подъем визитера по AST.
891 | // Сначала вызывается подписка на подчиненный узел, потом на этот, потом на родительский и так далее.
892 | Процедура ПокинутьОператорПрисваивания(ОператорПрисваивания) Экспорт
893 | // ...
894 | КонецПроцедуры
895 |
896 | #КонецОбласти
897 | ```
898 |
899 | ### Полный шаблон плагина
900 |
901 | ```bsl
902 | Перем Типы;
903 | Перем Токены;
904 | Перем Исходник;
905 | Перем ТаблицаТокенов;
906 | Перем ТаблицаОшибок;
907 | Перем ТаблицаЗамен;
908 | Перем Стек;
909 | Перем Счетчики;
910 | Перем Директивы;
911 | Перем Аннотации;
912 | Перем СимволыПрепроцессора;
913 |
914 | Перем Результат;
915 |
916 | Процедура Открыть(Парсер, Параметры) Экспорт
917 |
918 | Типы = Парсер.Типы();
919 | Токены = Парсер.Токены();
920 | Исходник = Парсер.Исходник();
921 | ТаблицаТокенов = Парсер.ТаблицаТокенов();
922 | ТаблицаОшибок = Парсер.ТаблицаОшибок();
923 | ТаблицаЗамен = Парсер.ТаблицаЗамен();
924 | Стек = Парсер.Стек();
925 | Счетчики = Парсер.Счетчики();
926 | Директивы = Парсер.Директивы();
927 | Аннотации = Парсер.Аннотации();
928 | СимволыПрепроцессора = Парсер.СимволыПрепроцессора();
929 |
930 | Результат = Новый Массив;
931 |
932 | КонецПроцедуры
933 |
934 | Функция Закрыть() Экспорт
935 | // ...
936 | Возврат СтрСоединить(Результат);
937 | КонецФункции
938 |
939 | Функция Подписки() Экспорт
940 | Перем Подписки;
941 | Подписки = Новый Массив;
942 | Подписки.Добавить("ПосетитьОператорПрисваивания");
943 | //Подписки.Добавить("ПокинутьОператорПрисваивания");
944 | Возврат Подписки;
945 | КонецФункции
946 |
947 | #Область РеализацияПодписок
948 |
949 | Процедура ПосетитьОператорПрисваивания(ОператорПрисваивания) Экспорт
950 | Ошибка("Ошибка в операторе присваивания", ОператорПрисваивания.Начало, ОператорПрисваивания.Конец);
951 | КонецПроцедуры // ПосетитьОператорПрисваивания()
952 |
953 | //Процедура ПокинутьОператорПрисваивания(ОператорПрисваивания) Экспорт
954 | //
955 | //КонецПроцедуры // ПокинутьОператорПрисваивания()
956 |
957 | #КонецОбласти
958 |
959 | Процедура Ошибка(Текст, Начало, Конец = Неопределено, ЕстьЗамена = Ложь)
960 | Ошибка = ТаблицаОшибок.Добавить();
961 | Ошибка.Источник = "ИмяЭтогоПлагина";
962 | Ошибка.Текст = Текст;
963 | Ошибка.ПозицияНачала = Начало.Позиция;
964 | Ошибка.НомерСтрокиНачала = Начало.НомерСтроки;
965 | Ошибка.НомерКолонкиНачала = Начало.НомерКолонки;
966 | Если Конец = Неопределено Или Конец = Начало Тогда
967 | Ошибка.ПозицияКонца = Начало.Позиция + Начало.Длина;
968 | Ошибка.НомерСтрокиКонца = Начало.НомерСтроки;
969 | Ошибка.НомерКолонкиКонца = Начало.НомерКолонки + Начало.Длина;
970 | Иначе
971 | Ошибка.ПозицияКонца = Конец.Позиция + Конец.Длина;
972 | Ошибка.НомерСтрокиКонца = Конец.НомерСтроки;
973 | Ошибка.НомерКолонкиКонца = Конец.НомерКолонки + Конец.Длина;
974 | КонецЕсли;
975 | Ошибка.ЕстьЗамена = ЕстьЗамена;
976 | КонецПроцедуры
977 |
978 | Процедура Замена(Текст, Начало, Конец = Неопределено)
979 | НоваяЗамена = ТаблицаЗамен.Добавить();
980 | НоваяЗамена.Источник = "ИмяЭтогоПлагина";
981 | НоваяЗамена.Текст = Текст;
982 | НоваяЗамена.Позиция = Начало.Позиция;
983 | Если Конец = Неопределено Тогда
984 | НоваяЗамена.Длина = Начало.Длина;
985 | Иначе
986 | НоваяЗамена.Длина = Конец.Позиция + Конец.Длина - Начало.Позиция;
987 | КонецЕсли;
988 | КонецПроцедуры
989 |
990 | Процедура Вставка(Текст, Позиция)
991 | НоваяЗамена = ТаблицаЗамен.Добавить();
992 | НоваяЗамена.Источник = "ИмяЭтогоПлагина";
993 | НоваяЗамена.Текст = Текст;
994 | НоваяЗамена.Позиция = Позиция;
995 | НоваяЗамена.Длина = 0;
996 | КонецПроцедуры
997 | ```
998 |
999 | ## Бакенды
1000 |
1001 | Бакенд реализуется в виде одного класса.
1002 |
1003 | Бакенды отличаются от плагинов тем, что не используют общий механизм обхода, а выполняют всю работу сами.
1004 |
1005 | ### Программный интерфейс бакенда
1006 |
1007 | Бакенд должен реализовать один метод:
1008 |
1009 | #### `Посетить(Парсер, АСД): Произвольный`
1010 |
1011 | *Выполняет произвольную работу и возвращает произвольный результат.*
1012 |
1013 | - `Парсер: Объект` -- экземпляр парсера.
1014 | - `АСД: Структура` -- абстрактное синтаксическое дерево.
1015 |
--------------------------------------------------------------------------------
/docgen.os:
--------------------------------------------------------------------------------
1 |
2 | #Использовать "./src"
3 | #Использовать "./examples/backends"
4 |
5 | ЧтениеТекста = Новый ЧтениеТекста(".\src\Классы\ПарсерВстроенногоЯзыка.os");
6 | Исходник = ЧтениеТекста.Прочитать();
7 |
8 | ГенераторДокументации = Новый ГенераторДокументацииДляПарсера;
9 |
10 | ПарсерВстроенногоЯзыка = Новый ПарсерВстроенногоЯзыка;
11 | АСД = ПарсерВстроенногоЯзыка.Разобрать(Исходник);
12 | Сайт = ГенераторДокументации.Посетить(ПарсерВстроенногоЯзыка, АСД);
13 | ПарсерВстроенногоЯзыка.Очистить();
14 |
15 | ЗаписьТекста = Новый ЗаписьТекста(".\docs\index.html");
16 | ЗаписьТекста.Записать(Сайт);
--------------------------------------------------------------------------------
/docs/ast.css:
--------------------------------------------------------------------------------
1 | html {
2 | scroll-behavior: smooth;
3 | margin-bottom: 25%;
4 | max-width: 950px;
5 | /* margin: auto; */
6 | margin-left: 50px;
7 | }
8 |
9 | body {
10 | font-family: Menlo, monospace;
11 | font-size: 14px;
12 | }
13 |
14 | .permalink {
15 | display: none;
16 | }
17 |
18 | :hover > .permalink {
19 | display: inline;
20 | color: black;
21 | }
22 |
23 | h2 {
24 | color:#963200;
25 | }
26 |
27 | pre {
28 | background: #dce5e9;
29 | width: 100%;
30 | padding: 15px;
31 | overflow: auto;
32 | min-width: 80ch
33 | }
34 |
35 | :target {
36 | background-color: #F0F0F0 ;
37 | margin: -4px ;
38 | padding: 4px ;
39 | border-radius: 4px ;
40 | outline: none ;
41 | }
42 |
43 | a:link, a:visited {
44 | text-decoration: none;
45 | color: blue;
46 | }
47 |
48 | a:hover, a:active {
49 | text-decoration: underline;
50 | color: blue;
51 | }
--------------------------------------------------------------------------------
/examples/backends/Классы/ГенераторДокументацииДляПарсера.os:
--------------------------------------------------------------------------------
1 |
2 | // Генератор технической документации по парсеру
3 |
4 | Перем Узлы;
5 | Перем Токены;
6 | Перем Типы;
7 | Перем Директивы;
8 | Перем Аннотации;
9 | Перем СимволыПрепроцессора;
10 | Перем ТаблицаТокенов;
11 | Перем Исходник;
12 |
13 | Перем Результат;
14 |
15 | Функция Посетить(Парсер, Параметры) Экспорт
16 |
17 | Узлы = Парсер.Узлы();
18 | Токены = Парсер.Токены();
19 | Типы = Парсер.Типы();
20 | Директивы = Парсер.Директивы();
21 | Аннотации = Парсер.Аннотации();
22 | СимволыПрепроцессора = Парсер.СимволыПрепроцессора();
23 | ТаблицаТокенов = Парсер.ТаблицаТокенов();
24 | Исходник = Парсер.Исходник();
25 |
26 | Результат = Новый Массив;
27 |
28 | Результат.Добавить(
29 | "
30 | |
31 | |
32 | |
33 | |Парсер встроенного языка
34 | |
35 | |
36 | |
37 | |
38 | |
Парсер встроенного языка
39 | |
40 | |На данной странице находится быстрая справка.
41 | |Руководство по парсеру находится в Книге Джедая.
42 | |
43 | |Настоящим Джедаям рекомендуется смотреть исходный код.
44 | |Только так можно достичь просветления.
45 | |
98 | |
99 | |Перем Результат;
100 | |
101 | |// Будет вызвана один раз перед обходом AST.
102 | |// Тут можно получить необходимые перечисления и таблицы из парсера,
103 | |// и выполнить инициализацию плагина с учетом полученных параметров.
104 | |Процедура Открыть(Парсер, Параметры) Экспорт
105 | | Результат = Новый Массив;
106 | |КонецПроцедуры
107 | |
108 | |// Будет вызвана после полного обхода AST.
109 | |// Возвращает текстовый результат работы плагина, если он есть.
110 | |// Плагины, которые регистрируют ошибки и/или замены, могут вернуть Неопределено.
111 | |Функция Закрыть() Экспорт
112 | | Возврат СтрСоединить(Результат);
113 | |КонецФункции
114 | |
115 | |// Возвращает список процедур-подписок, которые будут вызываться визитером.
116 | |// Состав возможных подписок можно посмотреть в исходнике парсера в функции Подписки().
117 | |// Имена большинства подписок образуются добавлением префикса Посетить/Покинуть к имени типа узла.
118 | |// Справка по типам узлов находится по этому адресу: https://oscript-library.github.io/osparser/
119 | |Функция Подписки() Экспорт
120 | | Перем Подписки;
121 | | Подписки = Новый Массив;
122 | | Подписки.Добавить(""ПосетитьОператорПрисваивания"");
123 | | Подписки.Добавить(""ПокинутьОператорПрисваивания"");
124 | | Возврат Подписки;
125 | |КонецФункции
126 | |
127 | |#Область РеализацияПодписок
128 | |
129 | |// Описание структуры узла `ОператорПрисваивания`: https://oscript-library.github.io/osparser/#ОператорПрисваивания
130 | |
131 | |// Данная процедура будет вызвана при посещении узла AST перед посещением подчиненных ему узлов.
132 | |// Вызов подписок с префиксом `Посетить` отражает рекурсивный спуск визитера по AST.
133 | |// Сначала вызывается подписка на родительский узел, потом на этот, потом на подчиненный и так далее.
134 | |Процедура ПосетитьОператорПрисваивания(ОператорПрисваивания) Экспорт
135 | | // ...
136 | |КонецПроцедуры
137 | |
138 | |// Данная процедура будет вызвана при посещении узла AST после посещения подчиненных ему узлов.
139 | |// Вызов подписок с префиксом `Покинуть` отражает рекурсивный подъем визитера по AST.
140 | |// Сначала вызывается подписка на подчиненный узел, потом на этот, потом на родительский и так далее.
141 | |Процедура ПокинутьОператорПрисваивания(ОператорПрисваивания) Экспорт
142 | | // ...
143 | |КонецПроцедуры
144 | |
145 | |#КонецОбласти
146 | |
147 | |
253 | | Таблица значений, которая хранит подробную информацию о всех токенах.
254 | | Каждый узел AST имеет поля Начало и Конец, которые содержат строки из этой таблицы.
255 | | С помощью обращения к таблице по индексу можно получить доступ к любому токену.
256 | | Комментарии при необходимости тоже извлекаются по данным этой таблицы.
257 | | Таблицу можно получить вызовом Парсер.ТаблицаТокенов()
258 | |
270 | | Таблица значений, которая хранит ошибки собранные парсером и плагинами.
271 | | Плагины могут получить таблицу в методе Открыть() и регистрировать в ней ошибки в процессе своей работы.
272 | | Таблицу можно получить вызовом Парсер.ТаблицаОшибок()
273 | | Обязательны к заполнению только первые 4 поля. Поле Код плагины не должны заполнять.
274 | | Если плагин регистрирует ошибку и замену ее исправляющую, то желательно установить ЕстьЗамена = Истина
275 | |
296 | | Таблица значений, которая хранит замены, регистрируемые плагинами.
297 | | Замена - это операция, аналогичная выделению участка текста и вставки фрагмента из буфера обмена.
298 | | Позиция и Длина указывают что выделить, а Текст - это вставляемый фрагмент.
299 | | Плагины могут получить таблицу в методе Открыть() и регистрировать в ней замены фрагментов исходника.
300 | | Фактические операции замены по таблице могут быть выполнены вызовом Парсер.ВыполнитьЗамены()
301 | | Таблицу можно получить вызовом Парсер.ТаблицаЗамен()
302 | |
" "");
320 |
321 | Для Каждого Объявление Из Узлы.ОбъявлениеМетода Цикл
322 |
323 | ДобавлятьЗаголовок = Ложь;
324 |
325 | Если Имена.Свойство(Объявление.Сигнатура.Имя, ДобавлятьЗаголовок) Тогда
326 |
327 | Если ДобавлятьЗаголовок Тогда
328 | Результат.Добавить(СтрШаблон("
%1
" "", Сред(Объявление.Сигнатура.Имя, 17)));
329 | КонецЕсли;
330 |
331 | Для Каждого Оператор Из Объявление.Операторы Цикл
332 |
333 | Если Оператор.Тип = Типы.ОператорВызоваПроцедуры Тогда
334 |
335 | Если Оператор.Идентификатор.Голова.Имя = "Парсер_Узлы" Тогда
336 |
337 | УзелИмя = Оператор.Идентификатор.Хвост[0].Аргументы[0].Элементы[0].Значение;
338 |
339 | СписокОписаний = Новый СписокЗначений;
340 |
341 | ДанныеСледующегоТокена = ТаблицаТокенов[Оператор.Конец.Индекс + 2];
342 | Пока ДанныеСледующегоТокена.Токен = Токены.Комментарий Цикл
343 | СписокОписаний.Добавить(Сред(Исходник, ДанныеСледующегоТокена.Позиция, ДанныеСледующегоТокена.Длина));
344 | ДанныеСледующегоТокена = ТаблицаТокенов[ДанныеСледующегоТокена.Индекс + 1];
345 | КонецЦикла;
346 |
347 | Результат.Добавить(СтрШаблон(
348 | "
",
445 | Имя
446 | ));
447 | ЗначенияПеречисления = Новый Структура;
448 | Для Каждого Элемент Из Перечисление Цикл
449 | ЗначенияПеречисления.Вставить(Элемент.Значение);
450 | КонецЦикла;
451 | Для Каждого Элемент Из ЗначенияПеречисления Цикл
452 | Если Ссылка Тогда
453 | Буфер.Добавить(СтрШаблон("