diff --git a/lib/rouge/demos/xbsl b/lib/rouge/demos/xbsl new file mode 100644 index 0000000000..6b09d90dec --- /dev/null +++ b/lib/rouge/demos/xbsl @@ -0,0 +1,8 @@ +метод Скрипт() + пер Результат = 0 + если Результат == 0 + для Счетчик = 1 по 5 + Результат += 1 + ; + ; +; diff --git a/lib/rouge/lexers/xbsl.rb b/lib/rouge/lexers/xbsl.rb new file mode 100644 index 0000000000..51c79b6845 --- /dev/null +++ b/lib/rouge/lexers/xbsl.rb @@ -0,0 +1,196 @@ +# -*- coding: utf-8 -*- # +# frozen_string_literal: true + +module Rouge + module Lexers + class XBsl < RegexLexer + title "XBSL" + desc "The 1C:Enterprise Element programming language" + tag 'xbsl' + filenames '*.xbsl', '*.sbsl' + + KEYWORDS = /\b(?: + вконце | finally | возврат | return + | выбор | case | выбросить | throw + | для | for | если | if + | знч | val | и | and + | из | in | или | or + | импорт | import | иначе | else + | исключение | exception | исп | use + | как | as | когда | when + | конст | const | конструктор | constructor + | метод | method | не | not + | неизвестно | unknown | новый | new + | обз | req | область | scope + | пер | var | перечисление | enum + | по | to | поймать | catch + | пока | while | попытка | try + | прервать | break | продолжить | continue + | статический | static | структура | structure + | умолчание | default | это | is + | этот | this + )\b/ix + + QUERY_KEYWORDS = /\b(?: + ВЫБРАТЬ | SELECT | ВСТАВИТЬ | INSERT | ИЗМЕНИТЬ | UPDATE | РАЗРЕШЕННЫЕ | ALLOWED + | ПЕРВЫЕ | TOP | РАЗЛИЧНЫЕ | DISTINCT | ОБРЕЗАТЬ | TRUNCATE | УДАЛИТЬ | DELETE + | УНИЧТОЖИТЬ | DROP | ПОМЕСТИТЬ | INTO | ИНДЕКСИРОВАТЬ | INDEX | УСТАНОВИТЬ | SET + | СОЗДАТЬ\\s+ВРЕМЕННУЮ\\s+ТАБЛИЦУ | CREATE\\s+TEMPORARY\\s+TABLE + | СОЗДАТЬ\\s+ИНДЕКС | CREATE\\s+INDEX + | ИЗ | FROM | ГДЕ | WHERE | СОЕДИНЕНИЕ | JOIN | ЛЕВОЕ | LEFT + | ПРАВОЕ | RIGHT | ПОЛНОЕ | FULL | ВНУТРЕННЕЕ | INNER | ОБЪЕДИНИТЬ | UNION + | ВСЕ | ALL | УПОРЯДОЧИТЬ | ORDER | СГРУППИРОВАТЬ | GROUP | ИМЕЮЩИЕ | HAVING + | ИЕРАРХИИ | HIERARCHY | ПО | ON | BY | ВОЗР | ASC | УБЫВ | DESC + | ДЛЯ | FOR | КАК | AS | ЕСТЬ | IS | В | IN + | ВЫРАЗИТЬ | CAST | ВЫБОР | CASE | КОГДА | WHEN | ТОГДА | THEN + | ИНАЧЕ | ELSE | КОНЕЦ | END | НЕ | NOT | ИЛИ | OR + | И | AND | ССЫЛКА | REFS | СПЕЦСИМВОЛ | ESCAPE |NULL + | ПОДОБНО | LIKE | МЕЖДУ | BETWEEN + )\b/ix + + state :root do + rule /(импорт|import)(\s+[\wа-яё\.]+)/i do |m| + token Keyword::Namespace, m[1] + token Name::Namespace, m[2] + end + + mixin :query + + mixin :statements + + mixin :value_types + + mixin :comments + + rule /@[\wа-яё]+\b/i, Name::Tag + rule KEYWORDS, Keyword + mixin :literals + rule %r/(\")/, Punctuation, :string_end + mixin :other_text + rule /[\(\)\[\]]/, Punctuation + end + + state :literals do + rule %r/(?<=[^\wа-яё\.]|^)((\d+\.?\d*)|(0x[0-9a-f]+)|(0b[0-1]+))(?=[^\wа-яё\.]|$)/, Literal::Number + rule %r/(Истина|True|Ложь|False|Неопределено|Undefined)/, Keyword::Constant + rule %r/(\b[\wа-яё]+\b)(\s*\{)(.*?)(\})/i do |m| + token Name::Class, m[1] + token Punctuation, m[2] + token Keyword::Constant, m[3] + token Punctuation, m[4] + end + end + + state :string_end do + rule %r/(\\.)/, Literal::String::Symbol + rule %r/([%\$])(?=\")/, Literal::String + rule %r/([^\"%\$]+?)/i, Literal::String + rule %r/([%\$][0-9\s])/, Literal::String + rule %r/([%\$](?:[\wа-яё]+|\{[^\}]+\}))/i, Generic::Inserted + rule %r/(\")/, Punctuation, :pop! + end + + state :other_text do + rule /\s+/, Text::Whitespace + rule /[\wа-яё]+/i, Name::Variable + rule %r/(->)/, Name::Function::Magic + rule %r/[:,;\|]/, Punctuation + rule %r/[-+\/*%=<>.?&!]/, Operator + rule %r/(?<=[^\wа-яё\.]|^)(не|not|и|and|или|or)(?=[^\wа-яё\.]|$)/, Operator + end + + state :comments do + rule %r(//.*$), Comment::Single + rule %r(\/\*[.\s\n\w\W]*?\*\/), Comment::Multiline + end + + state :query do + rule %r/\b(Запрос|Query)(\s*\{)/i do |m| + token Name::Class, m[1] + token Punctuation, m[2] + push :query_end + end + + rule %r/\b(новый|new)(\s*)\b(Запрос|Query)(\s*\(\s*\")/i do |m| + token Keyword, m[1] + token Text::Whitespace, m[2] + token Name::Class, m[3] + token Punctuation, m[4] + push :query_end_bracket + end + end + + state :query_end do + mixin :query_body + rule %r/(\s*\})/, Punctuation, :pop! + end + + state :query_end_bracket do + mixin :query_body + rule %r/(\s*\"\s*\))/, Punctuation, :pop! + end + + state :query_body do + mixin :comments + mixin :literals + rule %r/[%\$][\wа-яё]+\b/i, Generic::Inserted + rule %r/([%\$](?:[\wа-яё]+|\{[^\}]+\}))/i, Generic::Inserted + mixin :query_func + rule QUERY_KEYWORDS, Keyword + mixin :other_text + end + + state :query_func do + rule %r/\b([\wа-я]+\s*)(\()/i do |m| + token Name::Function, m[1] + token Punctuation, m[2] + push :query_func_end + end + end + + state :query_func_end do + mixin :query_body + rule %r/(\))/, Punctuation, :pop! + end + + state :statements do + rule %r/^(\s*(?:структура|structure|перечисление|enum)\s+)([\wа-яё]+)/i do |m| + token Keyword, m[1] + token Name::Entity, m[2] + end + + rule %r/\b(область|scope)\b/, Comment::Preproc + + rule %r/(попытка|поймать|исключение|try|catch|exception|вконце|finally)\s/, Name::Exception + + rule %r/(новый|new)(\s+[\wа-яё\.]+)/i do |m| + token Keyword::Declaration, m[1] + token Name::Class, m[2] + end + + rule %r/(const|конст)(\s+[\wа-яё]+)/i do |m| + token Keyword::Constant, m[1] + token Name::Constant, m[2] + end + + rule %r/\b(знч|val)(\s+[\wа-яё]+)/ do |m| + token Keyword::Constant, m[1] + token Name::Variable, m[2] + end + + rule %r/\b(пер|var|исп|use)(\s+[\wа-яё]+)/ do |m| + token Keyword::Variable, m[1] + token Name::Variable, m[2] + end + end + + state :value_types do + rule %r/(:\s*)([\wа-яё\?\.\ \t|<>]+)([,\)=]|$)/i do |m| + token Punctuation, m[1] + token Keyword::Type, m[2] + token Punctuation, m[3] + end + end + end + end +end diff --git a/spec/lexers/xbsl_spec.rb b/spec/lexers/xbsl_spec.rb new file mode 100644 index 0000000000..8c3d7571ab --- /dev/null +++ b/spec/lexers/xbsl_spec.rb @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- # +# frozen_string_literal: true + +describe Rouge::Lexers::XBsl do + let(:subject) { Rouge::Lexers::XBsl.new } + + describe 'guessing' do + include Support::Guessing + + it 'guesses by filename' do + assert_guess :filename => 'foo.xbsl' + end + end +end diff --git a/spec/visual/samples/xbsl b/spec/visual/samples/xbsl new file mode 100644 index 0000000000..504e4d1a0c --- /dev/null +++ b/spec/visual/samples/xbsl @@ -0,0 +1,193 @@ +import Библиотека /* тест */ + +импорт Товары.МодульОбъекта.ОписаниеТовара// dfdf + +конст Б = Истина + +/* Многострочный + комментарий +/* может содержать + любые символы * / */ +@ВПодсистеме @клиент //sdddsds +метод Метод1(Параметр1 : Тип1, + Параметр2: Массив<Строка | Число|?> = "Значение по умолчанию"): Тип2<Тип2<Тип3|Тип4>> + + пер А: Число + А = 15 // целое десятичное число + А = 3.141592654 // дробное десятичное число + А = 15/7 // выражение с использованием десятичных чисел + А = 0xff // шестнадцатеричное число + А = -0xff // отрицательное шестнадцатеричное число + А = 0b10101 // двоичное число + + пер б = а.Представление("'Результат: '6.2") + + // Полная форма - %{выражение} + пер Длина:Число = 100 + пер Ширина = 30 + пер Сообщение = "Площадь равна %{Длина * Ширина} м2" + + // С форматированием - ${выражение|формат} + пер Сейчас = ДатаВремя.Сейчас() + пер Сообщение = "Текущая дата ${Сейчас|дд ММММ гггг, дддд}" +; + +статический метод Скрипт() + пер а: Число = 2 + пер б: Число = 3 + б++ + Консоль.Записать(а > б) // Выдаст значение Ложь типа Булево + Консоль.Записать(а < 123) // Выдаст значение Истина типа Булево + + знч Стр1 = "%0" + знч Стр2 = "$0" + знч Стр3 = "50% заряда" + знч Стр4 = "цена 10$" + область + // вариант 1 + пер ИмяПользователя: Строка + пер Приветствие = "Привет, %ИмяПользователя!" + ИмяПользователя = "Ипполит" + ; +; + +метод РассчитатьСебестоимость(Нал_ог: Число = 1, //comment + Наценка: Число = 10, /*inline*/ Цена: Строка = "Строка"): Число + возврат Цена - (Цена * Наценка / 100 + Цена * Налог / 100) +; + +структура ПользовательПриложения + обз пер Переменная: Строка + пер Имя: Строка + пер Ид: Ууид + пер ДоступКПриложению: Булево = Истина + + // Возвращает значение: "Вторая строка" + знч СтрокаДва = новый Строка(Байты{feff04120442043e04400430044f0020044104420440043e043a0430}, + Кодировка.Utf16) + + ДанныеСообщений.Добавить("{\n \"date\" : \"%ДатаСообщения\",\n\"headers\" : %Заголовки,\n\"body\" : %Тело\n}") + + + если ФильтроватьПоВозрасту + СотрудникиПоследовательность = СотрудникиПоследовательность.Фильтровать(а -> а.Возраст > 30) + ; + + знч Запрос = Запрос{ВЫБРАТЬ + Лог.ЗаголовкиСообщения КАК Заголовки, + Лог.ТелоСообщения КАК Тело, + Лог.МеткаВремени КАК ДатаСообщения // комментарий + ИЗ + Лог КАК Лог + ГДЕ + Лог.ИдентификаторСодержимогоСообщения == %ИдентификаторОбъекта + //И Лог.МеткаВремени >= %{ДатаОт} + И Лог.МеткаВремени <= %ДатаДо + И Лог.МеткаВремени != 7 + } + + знч Запрос2 = новый Запрос(" + ВЫБРАТь ПЕРВЫЕ 1 Поле, Поле2 ИЗ Таблица ГДЕ Поле НЕ ЕСТЬ NULL + ИМЕЮЩИЕ ПО СУММА(Поле2 * ВЫБОР КОГДА ИСТИНА НЕ ЕСТЬ NULL ТОГДА 1 ИНАЧЕ 42 КОНЕЦ) > 42 + / КОЛИЧЕСТВО(%Делитель) + ") + + пер КонкретныйИдентификатор = новый Ууид("550e8400-e29b-41d4-a716-446655440000") + пер КонкретныйИдентификатор2 = Ууид{550e8400-e29b-41d4-a716-446655440000} + + метод Скрипт() + пер МоментСоздания = новый ДатаВремя(2019, 05, 01, 23, 30, 40, 345) + пер МоментСозданияФормат = МоментСоздания.ВМомент(ЧасовойПояс{UTC}) + + пер ДатаСоздания = новый ДатаВремя(2019, 5, 1, 23, 30, 40) + пер ДатаСозданияФормат = ДатаСоздания.Представление("дд ММММ гггг', 'ЧЧ:мм:сс") + + пер Период = Строки.Шаблон("С %0 по %1 года", [ДатаНачалаФормат, ДатаОкончанияФормат]) + + пер ОкончаниеРаботы = Время{18:00} + + пер Значение3: Строка|Число|Неопределено = МоментСоздания("ы") + ; + + метод НайтиПользователя(Ид: ИдПользователя): ОписаниеПользователяСервиса + знч ОписаниеПользователя = ПользователиСервиса.Найти(Ид) + + // Метод вернет "никогда", если "ОписаниеПользователя" неопределено + возврат ОписаниеПользователя != Неопределено ? ОписаниеПользователя : ВыброситьИсключение("Пользователь не найден!") + ; +; + +метод ЭтоМассив(Параметр: Объект) + пер ТипПараметра = Параметр.ПолучитьТип() + пер Результат: Строка + если Параметр это не ЧитаемыйМассив<Объект> + Результат = "Это не массив!" + иначе если Параметр это ЧитаемыйМассив<Строка> + Результат = "Это массив строк!" + иначе если Параметр это ЧитаемыйМассив<Число> + Результат = "Это массив чисел!" + иначе + Результат = "Это массив!" + ; + + пер Значение = Ссылка.ЗагрузитьОбъект()?.РеквизитСсылка?.ЗагрузитьОбъект()?.РеквизитЧисло ?? 0 +; + + +метод ПримерНаЗакрываемыйТип() + попытка + пер Файл = Файлы.Создать(СредаИсполнения.ПолучитьПеременную("temp") + "\\test.txt") + исп Поток = Файл.ОткрытьПотокЗаписи() + Поток.Записать("1-я строка\н") + Поток.Записать("2-я строка\н") + Поток.Записать("3-я строка\н") + пер Успех = "Файл записан!" + поймать Искл: Исключение + // попадем сюда, если хоть какое-то исключение случилось + // здесь поток записи уже закрыт + // переменная Поток здесь уже отсутствует + пер Ошибка = Искл.ПоследовательностьВызовов + "\н" + Искл.Описание + вконце + // попадем сюда в любом случае + // здесь поток записи уже закрыт! + // переменная Поток здесь уже отсутствует + пер Конец = "Файловый поток закрыт, переменная недоступна (выход из области видимости)" + ; + + пер А = Момент{2019-05-01 23:30:40.345 UTC+3} + пер Б = Момент(Дата{2019-05-01},Время{23:30:40},ЧасовойПояс{UTC+3}) +; + +перечисление ОперационныеСистемы + Linux, + macOS, // для ОС Apple + Windows умолчание + + @сервер + метод МетодПеречисления(): Булево + ; + + статический метод МетодПеречисления2(): Булево + ; +; + + +структура Продажа + обз пер Товар: Строка = "Пустая" + обз пер Дата: Дата + + @ИменованныеПараметры + конструктор + + // Простой метод + метод ВСтроку(): Строка + возврат Товар + " " + Дата + ; + + // Статический метод + @Аннотация1(Параметр11 = @Аннотация2(Параметр21 = "ЗначениеПараметра")) + статический метод ИзСтроки(СтрокаПродажи: Строка): Продажа + знч Части = СтрокаПродажи.Разделить(" ") + возврат новый Продажа(Части[0], новый Дата(Части[1])) + ; +;