Программирование в 1С для всех
В этой статье изучим один из примитивных типов платформы 1С 8.3 – тип Строка. Научимся выводить строки, соединять, переносить, объединять с другими примитивными типами, а также узнаем, как работают строки в серверном и клиентском контексте, и научимся работать со строками на управляемой форме и в реквизитах объектов.
Строка в 1С
Строка это примитивный тип. Создать переменную типа строка достаточно просто. Нужно переменной присвоить какое-то значение в кавычках.
Метод Сообщить в 1С
Самый простой вариант вывести какую-то строку, это использование процедуры Сообщить. Это процедура глобального контекста, которая выводит сообщение пользователю внизу формы.
В параметре этой процедуры указывается переменная с типом строка (или сама строка), которая будет выведена пользователю на форме.
Если мы код выше применим на какой-нибудь управляемой форме, то получим такое сообщение.
Как объединить строки в 1С
В платформе 1С 8.3 имеется возможность объединять строки. Эта операция называется конкатенация, и выполняется при помощи оператора +.
Можно объединить строки, просто сложив две строки в кавычках. Вот так
Также, можно сложить две переменных с типом строка.
Объединять можно и несколько строк.
Перенос строки в 1С
Все строки, которые мы делали ранее, представляли собой одну длинную строку, что, согласитесь, бывает иногда не совсем удобно. Чтобы не испытывать неудобства, в платформе 1С можно переносить строки, тем самым получая многострочные строки. Для этих целей имеется символ переноса строки – «|» (вертикальная черта).
Наша строка с переносом должна иметь следующий вид.
В случае переноса, после окончания ряда строки не должно быть ни каких символов, а каждая новая строка должна начинаться с символа переноса строки «|».
Если мы выведем эту строку с помощью уже знакомого нам метода Сообщить, то будет такой результат.
Причём, обращу ваше внимание, что не достаточно просто написать символ «|» в строке, нужно этот символ, именно, перенести на новую строку в коде. Поэтому при написании такой строки:
Будет выводиться неверный результат:
Кавычки в строке в 1С
Иногда необходимо внутри строки отобразить кавычку как символ, как это сделать, если у нас кавычка открывает или закрывает строку. Для того, чтобы в строке вывелась кавычка необходимо в строке написать две кавычки подряд.
Будет выдавать следующий результат:
Пустая строка в 1С
А если нам нужно задать пустую строку? В этом случае достаточно просто написать две кавычки рядом, но так, чтобы между ними не было пробела.
То есть вот так.
Также, в платформе 1С 8.3. имеется возможность проверить, является строка пустой или нет. Для этого необходимо воспользоваться функцией ПустаяСтрока, где в качестве параметра нужно указать проверяемую строку, а возвращает она Истину, или Ложь, в зависимости от того, пустая строка или нет.
Также, эта функция вернет Истина, если в строке одни пробелы.
В этом примере я воспользовался отладкой в коде.
Перевод примитивных типов в строку
Часто возникает задачи, когда нужно примитивный тип перевести в строку, это можно сделать, используя метод Формат, или просто применить метод глобального контекста Строка, где в качестве параметра указать значение примитивного типа. При помощи этого метода можно перевести число в строку, дату в строку и тип булево в строку.
В коде ниже, преобразуются число, булево значение Истина и дата (текущая дата) в строку, а потом все это выводится в сообщении.
У этого кода будет следующий результат:
Причем, в методе Сообщить мы можем в некоторых случаях указать и переменную не строкового типа.
Также можно операцией конкатенацией соединять переменную типа строка с примитивной переменной не строкового типа.
В обоих случаях код нормально отработается.
Конкатенацией мы можем соединить строку с другим примитивным типом, но не можем, к примеру, соединить дату и булево.
Рекомендую вам не злоупотреблять конкатенацией с другими типами, иногда это может приводить к критическим ошибкам.
Строка 1С в клиентском и серверном контексте
Переменную примитивного типа строка можно создать, как в серверном, так и в клиентском контексте. Такой код будет вполне рабочий.
И также, для строк имеется возможность сериализации: т.е. можно перенести переменную с типом строка с клиента на сервер и наоборот:
Поэтому, и такой код будет вполне рабочим:
И наоборот, код также будет работать:
Таким образом, переменную с примитивным типом строка мы можем передать как с клиента на сервер, так и наоборот – с сервера на клиент.
Использование типа Строка в реквизитах объектов
Для того, чтобы реквизит какого-нибудь объекта имел тип Строка, достаточно просто указать этот тип в свойстве Тип нужного реквизита объекта.
У реквизита со свойством строка мы можем указать максимальную длину строки, а также будет длина строки фиксированная или переменная. Максимальная длина – это максимальное количество символов, которое можно вписать в строку.
Если мы укажем, что длина строки фиксированная, то длина строки реквизита объекта всегда будет приводиться к максимальной (заполняться пробелами).
Например, у реквизита ПолноеНаименование справочника Товары, я указал длину строки 10, и также отметил, что это фиксированная длина.
Если я запишу какое-то значение в поле формы, от оно автоматически заполнится пробелами до конца строки, и точно также с пробелами запишется в базу данных.
А если бы мы указали, что допустимая длинна – переменная.
То в этом случае, к строке не добавляются пробелы, и в базу записывается только то, что мы написали.
Также у строки можно указать, что она имеет неограниченную длину
В этом случае в строку можно вписать любое количество символов. Рекомендуется крайне редко использовать свойство Неограниченная длина, поскольку это свойство не совсем положительно сказывается на производительности.
Использование типа строка на управляемой форме
Реквизит управляемой формы 1С также может иметь тип Строка. Достаточно это указать в свойстве Тип реквизита.
Причем нам нужно нажать на кнопку «…» свойства, чтобы получить доступ к дополнительным свойствам строки.
В открывшемся окне мы можем указать длину строки, и установить (или снять) признак неограниченной длинны строки.
Если мы снимем признак неограниченности длинны строки, то сможем указать, какая будет строка – фиксированная или переменна.
Если мы перетащим реквизит в элементы формы, то будет создано поле ввода, в которое пользователь может вводить любую строковую информацию.
У нас имеется возможность сделать многострочный режим ввода, если у элемента поле ввода установим в свойство Многострочный режим значение Да.
Другие статьи о примитивных типах:
Более подробно и основательно начальные вопросы программирования в 1С есть вы можете изучить в
Книга «Программировать в 1С за 11 шагов»
Изучайте программирование в 1С в месте с моей книгой «Программировать в 1С за 11 шагов»
Книга написана понятным и простым языком — для новичка.
- Книга посылается на электронную почту в формате PDF. Можно открыть на любом устройстве!
- Научитесь понимать архитектуру 1С;
- Станете писать код на языке 1С;
- Освоите основные приемы программирования;
- Закрепите полученные знания при помощи задачника.
О том как разрабатывать под управляемым приложением 1С, читайте в книге Книга «Основы разработки в 1С: Такси»
Отличное пособие по разработке в управляемом приложении 1С, как для начинающих разработчиков, так и для опытных программистов.
- Очень доступный и понятный язык изложения
- Книга посылается на электронную почту в формате PDF. Можно открыть на любом устройстве!
- Поймете идеологию управляемого приложения 1С
- Узнаете, как разрабатывать управляемое приложение;
- Научитесь разрабатывать управляемые формы 1С;
- Сможете работать с основными и нужными элементами управляемых форм
- Программирование под управляемым приложением станет понятным
Промо-код на скидку в 15% — 48PVXHeYu
Эти книги, плюс книга по программированию оперативного учета имеются в едином комплекте: комплект книг по разработке в 1С.
Только для читателей моего блога,
промо-код на скидку в 300 рублей на весь комплект: blog
Если Вам понравился этот урок, был полезен или помог решить какую-нибудь проблему, то Вы можете поддержать мой проект, перечислив любую сумму.
можно оплатить вручную:
Яндекс.Деньги — 410012882996301
Web Money — R955262494655
Парные и непарные кавычки
Типографика. Оформление текстов
Кавычки — парный знак препинания, который употребляется для выделения прямой речи, цитат, отсылок, названий литературных произведений, газет, журналов, предприятий, а также отдельных слов, если они включаются в текст не в своём обычном значении, используются в ироническом смысле, предлагаются впервые или, наоборот, как устаревшие и т. п.
Так же как и скобки, кавычки бывают левыми и правыми. В языках программирования и разметки, однако, используются «программистские» кавычки, что оправдано с точки зрения быстроты набора и предназначения.
В русском языке традиционно применяются французские «ёлочки», а для кавычек внутри кавычек и при письме от руки — немецкие «лапки». Кроме того, при переводе значения иноязычного слова иногда также употребляют английские одиночные (или так называемые марровские) кавычки (‘ ’).
Поиск ответа
Всего найдено: 23
Здравствуйте! Скажите, пожалуйста, как следует писать название остановки со включённым в него названием объекта, пишущегося в кавычках. В двойных кавычках или без? Например, в объявлениях в транспорте: Следующая остановка — «Метро «Библиотека имени Ленина»?
Ответ справочной службы русского языка
Если есть техническая возможность, следует использовать кавычки разного рисунка: «Метро «Библиотека имени Ленина»». Если такой возможности нет, двойные кав ычки не ставятся: «Метро «Библиотека имени Ленина».
Добрый день! У меня вопрос о кавычках: в сложных предложениях встречается применение двойных кавычек, т.е. первая часть начинается из внешних кавычек, в этой части еще необходимо что-то выделить кавычками, например, наименование, и вся эта сложная конструкция должна закончиться двойным закрытием кавычек. Следует ли применять двойные кав ычки, как в математическом синтаксисе? Спасибо!
Ответ справочной службы русского языка
В таких случаях лучше применять кавычки разного рисунка, например: «»Автоваз» будет развиваться и дальше», – сказал он.
Добрый день! Ставятся ли двойные кав ычки в начале прямой речи, когда первое слово идет в кавычках? Например, «»Автоваз» бцдет развиваться и дальше», — сказал он. Благодарю за ответ. Сергей
Ответ справочной службы русского языка
Если есть техническая возможность, следует использовать кавычки разного рисунка: «»Автоваз» будет развиваться и дальше», – сказал он. Если такой возможности нет, двойные кав ычки не ставятся: «Автоваз » будет развиваться и дальше», – сказал он.
ставятся ли двойные кав ычки после названия компании в конце цитаты, если нет возможности поставить кавычки разного формата?
Ответ справочной службы русского языка
В таком случае используются одинарные кавычки.
С вашего позволения, попробую еще раз задать вопрос, который не дает мне покоя. Нужно ли на обложке книги брать в кавычки название, состоящее лишь из наименования, подлежащего закавычиванию в тексте этой книги? Например, если роман называется «Уют», «Идеал», «Советский» или «Гранд» по названию гостиницы, в котором происходит действие романа, стоит ли так и писать на обложке книги: «Уют», «Идеал» и т. д.?
Кроме того, возможное закавычивание не означает ли, что в условной рецензии на эту книгу следует использовать ужасные двойные кав ычки: «”Уют”», «”Идеал”» и т. п.?
Существуют ли какие-либо указания на этот счет? К сожалению, мне не удалось найти ответов на эти вопросы ни в справочниках, ни в интернете. Но может быть, я что-то и упустил.
Буду рад вашему ответу.
Ответ справочной службы русского языка
Кавычки, указывающие на то, что заголовок является собственным условным наименованием, будут уместны на обложке книги. Двойные кав ычки в рецензии избыточны.
Здравствуйте. Спор вышел с коллегами, я говорю, что в интернет-газете кавычки должны ставиться как в обычном печатном издании: по краям «ёлочки», внутри немецкие «лапки» (пример 1). Мне возражают, что «ведущие» интернет-газеты ставят три «ёлочки» (№ 2) или компьютерные кавычки (№ 3), и это нормально для Интернета. Я отвечаю, что если есть техническая возможность (а она ЕСТЬ), надо ставить двойные кав ычки как положено. Что скажете?
1. ФГУП «Российский научный центр „Прикладная химия“» (классич. двойные кав ычки)
2. ФГУП «Российский научный центр «Прикладная химия»
3. ФГУП «Российский научный центр «Прикладная химия»(«)
Ответ справочной службы русского языка
Совсем плох третий вариант с двумя знаками в конце предложения. В остальном — вопрос не орфографический, не лингвистический. Скорее, вопрос типографской эстетики. Как лучше, Вы знаете: немецкие «лапки», конечно, отличный вариант для верстки, но есть ли «руки», чтобы их последовательно проставлять?
Подскажите пожалуста, предусмотрены ли в русском языке двойные кав ычки (т.е. когда название в названии)
Ответ справочной службы русского языка
Подскажите, пожалуйста, принято ли ставить подряд двойные кав ычки -«елочки», т.е если в цитате есть еще одна цитата, заканчивающаяся там же, где первая.
Ответ справочной службы русского языка
Кавычки одного рисунка рядом не повторяют. По возможности используют кавычки разного рисунка: . «».
Подскажите, пожалуйста, если фраза заключена в кавычки, а последнее слово фразы также заключено в кавычки, в конце ставятся двойные кав ычки или одни?
Спасибо.
Ответ справочной службы русского языка
Можно либо использовать кавычки разного начертания, либо закрыть фразу одними закрывающими кавычками.
Здравствуйте, подскажите, пожалуйста, ставятся ли в русском языке двойные кав ычки и, если ставятся, то в каких случаях? В частности, столкнулся с такой ситуацией: в Беларуси существует организация, название которой пишется так: «Движение «За Свободу». Надо ли здесь ставить две закрывающие кавычки в конце или ставится одна? И надо ли ставить две открывающие кавычки, если с названия организации начинается цитата?
Ответ справочной службы русского языка
Следует использовать внутренние кавычки другого рисунка («лапки» вместо >) или избегать скопления кавычек. Если эти приемы невозможны, допускается «непарное» число кавычек.
Спасибо за ответ. Но, вероятно, я не совсем корректно задала вопрос. Нужно ли ставить двойные кав ычки: ООО «Косметическая фирма «Солнышко»». Спасибо
Ответ справочной службы русского языка
В таком случае предпочтительно использовать кавычки разного рисунка: ООО «Косметическая фирма «Солнышко»» . Если по каким-то причинам это невозможно, допустимо писать: ООО «Косметическая фирма «Солнышко». Кавычки одного рисунка рядом не повторяются.
Здраствуйте! Скажите пожалуйста, как правильно ставить двойные кав ычки в названиях. Ставятся ли закрывающие ковычки 2 раза или один?. Спасибо
Ответ справочной службы русского языка
Если в предложении необходимо использовать двойные кав ычки, можно ли использовать одни и те же, например:
Задача «Работа по программе «Восстановление работы ног»»
или следует использовать разные, например:
Задача «Работа по программе «Восстановление работы ног»»
Ответ справочной службы русского языка
Предпочтительно использовать кавычки разного рисунка, но если по техническим причинам это невозможно, то не возбраняется использовать и кавычки одного рисунка (но при этом следует помнить, что кавычки одного рисунка рядом не повторяются: задача «Работа по программе «Восстановление работы ног» ).
Здравствуйте! Вам пишут из редакции электронной газеты. У нас принят один вид кавычек — » «. И постоянно возникает вопрос, как оформлять в таком случае преложения типа: «Сегодня мы познакомимся с историей написания романа «Война и мир»», — сказал учитель. Нужны ли двойные кав ычки в конце или достаточно одних? Спасибо.
Ответ справочной службы русского языка
Вторые кавычки не нужны: _»Сегодня мы познакомимся с историей написания романа «Война и мир», — сказал учитель._
Подскажите, пожалуйста, ставятся ли в предложении двойные кав ычки (по типу двойных скобок)?
Ответ справочной службы русского языка
источники:
http://orfogrammka.ru/%D1%82%D0%B8%D0%BF%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D0%BA%D0%B0/%D0%BF%D0%B0%D1%80%D0%BD%D1%8B%D0%B5_%D0%B8_%D0%BD%D0%B5%D0%BF%D0%B0%D1%80%D0%BD%D1%8B%D0%B5_%D0%BA%D0%B0%D0%B2%D1%8B%D1%87%D0%BA%D0%B8/
http://new.gramota.ru/spravka/buro/search-answer?s=%D0%B4%D0%B2%D0%BE%D0%B9%D0%BD%D1%8B%D0%B5%20%D0%BA%D0%B0%D0%B2
Совмещение
Зарплата и кадры государственного учреждения, редакция 3.1 (3.1.13.219)
Здравствуйте ! Создаю сотруднику документ «Совмещение профессий» — Совмещение должностей — выбираю должность .Выходит ошибка»выходит ошибка — «словосочетание содержит непарные скобки или кавычки». При выборе этой же должности н-р в кадровом перемещение ничего подобного не происходит
Эти
действия осуществляются в среде
программирования QBASIC.
Для входа в главное меню необходимо
нажать клавишу ALT.
Затем нажать подсвеченную букву пункта
меню и выбрать нужный пункт под меню.
Если пункт подменю оканчивается
троеточием «…», что при выборе
этого пункта возникнет диалоговое окно.
Главное
меню включает пункты «ФАЙЛ, РЕДАКТИРОВАНИЕ,
ПРОСМОТР, ПОИСК, ЗАПУСК, ОТЛАДКА,
ПАРАМЕТРЫ, СПРАВКА». Рассмотрим содержание
пунктов меню:
|
|
|
|
|
|
|
Среда
программирования QBASIC
представляет собой интеллектуальный
редактор, осуществляющий синтаксический
контроль вводимого текста. Если
строка программы, набранная строчными
буквами, написана правильно, то после
перевода курсора на следующую строку
все ключевые слова QBASIC
будут написаны заглавными буквами,
операторы отделены друг от друга.
Это позволяет сразу понять, есть ли в
строке ошибки.
При
редактировании удаление текста слева
от курсора производится клавишей
Backspace
(),
а справа — клавишей Del.
Чтобы копировать, вырезать, удалить
текст, его предварительно необходимо
выделить. Выделяется текст с помощью
комбинации клавиши Shift
+ клавиши со стрелками или с нажатой
левой клавишей мыши. Приемы ввода текста
мало отличаются от работы в других
редакторах. Наиболее применяемые команды
меню дублируются комбинациями клавиш
(см. таблицу 0.0.5).
Среда
программирования QBASIC
позволяет запустить программу на
выполнение. Если появилось сообщение
об ошибке, и она понятна, следует щелкнуть
по кнопке OK,
при необходимости уточнений выбрать
Справку
или обратиться к таблице 0.0.6.
Вот
некоторые приемы, применяемые при
отладке программ. Например, если программа
по 2-й лабораторной работе дает неправильные
значения, то это может быть по двум
причинам: либо неправильно реализованы
формулы, либо не реализовано ветвление
и программа считает не по той формуле.
Чтобы проверить второй вариант, нужно
воспользоваться клавишей F8,
высвечивающей траекторию движения по
программе. При этом строки с оператором
IF…THEN
следует сделать многооператорными,
поставив в конце пустой PRINT.
Если какой-либо оператор мешает
разобраться в причинах появления ошибки,
то его можно вывести из рассмотрения,
не стирая, поставив перед ним оператор
REM.
А чтобы вывести из рассмотрения большой
фрагмент программы, следует использовать
GOTO
с указанием номера строки, куда следует
перепрыгнуть. Чтобы разобраться с
циклом, например, выяснить, правильно
ли считается сумма, можно в него вставить
оператор PRINT,
поставив за ним SLEEP,
организующий паузу до нажатия любой
клавиши. Впрочем, с целью создания паузы
можно применять и просто SLEEP.
Проверить, работает ли цикл или иной
фрагмент программы, можно, использовав
PRINT
«Я здесь».
Таблица 0.0.5.
Назначение |
|
Клавиши |
Назначение |
F1 |
Справка |
Shift+F1 |
Вывод |
F2 |
Вывод |
Sfift+F2 |
Вывод |
Ctrl+F2 |
Вывод |
F3 |
Повтор |
F4 |
Переход |
F5 |
Продолжение |
Shift+F5 |
Запуск |
F6 |
Переброс |
Shift+F6 |
Переброс |
F7 |
Выполнение |
F8 |
Пошаговое |
F9 |
Установка |
F10 |
Пошаговое |
Shift+ |
Выделение |
Shift |
Вырезание |
Ctrl |
Вырезание |
Shift |
Вставка |
Ctrl |
Копирование |
Ctrl |
Русский |
Ctrl +Break |
Приостановка |
Примечание: Комбинация затем, не отпуская Ctrl, |
Таблица 0.0.6.
Код |
Сообщение об |
1 |
NEXT Для |
2 |
Синтаксическая Оператор |
3 |
RETURN Для |
4 |
Нет В |
5 |
Неверный Возникает |
6 |
Переполнение Числовая |
7 |
Не |
8 |
Метка Для |
9 |
Индекс Сообщение |
10 |
Повторяющееся Может |
Продолжение таблицы
0.0.6.
11 |
Деление Выражение |
12 |
Ошибка в режиме |
13 |
Ошибка ввода |
14 |
В |
16 |
Слишком (String |
17 |
Невозможно |
18 |
Функция Возможно, |
19 |
Нет |
20 |
RESUME |
24 |
Устройство |
25 |
Ошибка |
26 |
FOR Для |
27 |
Нет |
29 |
WHILE Для |
30 |
WEND Для |
33 |
Повторяющаяся При |
35 |
Подпрограмма не Сообщение |
37 |
Ошибка счетчика |
38 |
Массив не определен Попытка |
39 |
Требуется |
40 |
Необходима Возникает |
Продолжение таблицы
0.0.6.
50 |
Переполнение |
51 |
Внутренняя Чаще |
52 |
Плохое (Bad Имя |
53 |
Файл При |
54 |
Плохой Возникает, |
55 |
Файл Попытка |
56 |
Оператор |
57 |
Ошибка Ошибка |
58 |
Файл Попытка |
59 |
Неверная |
61 |
Диск Диск, |
62 |
Ошибка: Возникает, |
63 |
Неверный |
64 |
Плохое Имя |
67 |
Слишком |
68 |
Устройство В |
Окончание таблицы
0.0.6.
69 |
Переполнение (Communication-buffer Попытка |
70 |
Нет |
71 |
Ошибка Открыта |
72 |
Ошибка В |
73 |
Недоступная |
74 |
Переименование |
75 |
Ошибка |
76 |
Путь При |
Пишем простой транслятор на Лиспе — I +17
Lisp
Рекомендация: подборка платных и бесплатных курсов создания сайтов — https://katalog-kursov.ru/
Давайте попробуем написать на Лиспе… транслятор простого императивного языка. Нет-нет, я не ошибся – именно транслятор. Транслировать он будет в Лисп-код. А дальше этот код может быть выполнен Лисп-системой.
Здесь бесценную услугу нам окажет то обстоятельство, что в Лиспе нет барьера между кодом и данными (это редкое свойство некоторых языков программирования называется “гомоиконность”). Но и изобразительные возможности Лиспа тоже сыграют не последнюю роль.
В качестве реализации я буду использовать HomeLisp. Желающие смогут адаптировать этот проект под Common Lisp. Скажу сразу – применительно к рассматриваемой задаче существенная разница между Common Lisp и HomeLisp состоит только в обработке строк и файлов.
Скачать портабельную версию HomeLisp можно по ссылке. На этом же сайте расположена и документация. Желающие могут копировать код из статьи и сразу проверять работоспособность.
Предлагаемая вашему вниманию тема послужила основой для моей мастерской на знаменитой Новосибирской ЛШЮП-2018. С результатами мастерской можно ознакомиться здесь. А далее я излагаю свой подход. Предполагаю, что читатель знаком с языком Лисп.
Приступаем
Начнем с “простого императивного языка”, который мы будем транслировать в Лисп.
Язык будет обрабатывать только числовые данные. Код на этом языке состоит из функций (процедур, возвращающих значения). Среди этих функций одна должна называться main. Именно с этой функции начинается выполнение кода. Хотя зачем так себя связывать? Пишем функции на императивном языке, они транслируются в Лисп и могут использоваться вместе с лисповскими функциями. Но не будем забегать вперед…
Набор операторов языка обычен: присвоение, ветвление, арифметический цикл, досрочный выход из цикла, ввод, вывод и вызов функции. Впрочем, синтаксически вызов функции оформляется как присвоение (результата вызова). Комментарии пусть содержат звездочку в первой позиции строки. Язык, разумеется, должен обеспечивать возможность создания рекурсивных функций. Чтобы стало понятнее, приведу примеры кода – печать последовательных нечетных чисел и вычисление их суммы:
proc main()
local s,n,k
input n
for i=1 to n
k=2*i-1
print k
s=s+k
end_for
print s
end_proc
По своему духу – это бэйсик-подобный язык. Я буду называть его “мини-бэйсик”. Наш транслятор должен преобразовать приведенный код в следующую Лисп-функцию:
(defun main nil
(let ((s 0) (n 0) (k 0))
(setq n (read))
(iter (for i from 1 to n)
(setq k (- (* 2 i) 1))
(printline k)
(setq s (+ s k)))
(printline s)))
Мне очень нравится пакет iterate, который в профессиональных пакетах Common Lisp реализован в виде макро. В HomeLisp функция iter (реализующая значительную часть возможностей макро iterate) включена в ядро языка. Мое пристрастие к iter и послужило причиной того, что циклы нашего “мини-бэйсика” будут транслироваться в вызовы iter.
С чего начать реализацию? Давайте начнем ее с выбора файла, подлежащего трансляции и с построчного чтения и печати этого файла. Запускать транслятор нам придется множество раз, так пусть этот запуск с самого начала будет удобным. Вот как может выглядеть эта функция:
(defun start (&optional (fname nil))
(setq *numline* 0)
(setq *flagerr* nil)
(when (null fname)
(setq fname (sysGetOpenName (sysHome) "Мини-бэйсик|*.mbs")))
(let ((fi (gensym 'fi)))
(when fname
(filOpen fi fname _INPUT)
(loop
(getLine fi)
(when (or *flagerr* (filEOF fi)) (return t)))
(filClose fi)
(when *flagerr* (printsline "**** Были найдены ошибки"))))
(unset '*numline*)
(unset '*flagerr*))
Функция имеет необязательный параметр fname – имя файла, содержимое которого будет транслироваться. При входе в функцию создаются две глобальные переменные numLine номер строки исходного файла и flagerr — флаг состояния ошибки. Перед завершением функции эти переменные уничтожаются (функция HomeLisp-а unset уничтожает глобальные переменные).
Если имя входного файла опущено – то вызывается стандартный windows-диалог выбора файла (sysGetOpenName). В качестве стартовой директории используется текущая директория (sysHome). Далее создается уникальный символ для файлового манипулятора и файл открывается для текстового чтения. Затем в бесконечном цикле производится чтение файла строка за строкой (функция getLine). После каждой операции проверятся, не возникла ли ошибка, и не достигнут ли конец файла. Если возникла ошибка или зафиксирован конец файла – цикл разрывается, файл закрывается, и, если были ошибки – выводится итоговое сообщение.
Собственно чтение из файла выполняет функция getLine:
(defun getLine (fil)
(let ((stri ""))
(loop
(when (filEof fil) (return ""))
(setq *numline* (add1 *numline*))
(setq stri (filGetline fil))
(printsline (strCat (format *numline* "0000") " "
(strRTrim stri)))
(setq stri (strATrim stri))
(unless (or (eq "" stri) (eq "*" (strLeft stri 1)))
(return stri)))))
Эта функция принимает идентификатор открытого файла и в бесконечном цикле выполняет следующие действия:
- проверяет состояние “конец файла”. В этом случае цикл разрывается и функция возвращает пустую строку;
- увеличивается на единицу счетчик прочитанных строк;
- читается очередная строка файла;
- прочитанная строка печатается с удалением возможных пробелов справа;
- если прочитанная строка непуста и не содержит звездочку в первой позиции, то она
возвращается из функции;
Таким образом, в выходной листинг попадают все строки файла в их исходном виде.
Разбиваем на процедуры
А теперь давайте научим наш код разбивать входной поток на отдельные процедуры. Сначала введенную строку потребуется разбить на лексемы (неделимые входные лексические единицы). Этот процесс называется парсингом; нам предстоит создать парсер. Написание парсеров — классическая тема, существуют библиотеки готовых парсеров и специальные средства, позволяющие сгенерировать нужный парсер… Мы пойдем своим путем.
Прежде, чем описывать алгоритм парсера, обратим внимание на то, что все символы входной строки можно поделить на два класса:
- Обычные символы;
- Символы-разделители.
Так, в операторе присвоения “x = 15 + y^2” символы x,1,5,y и 2 – есть обычные символы, а символы “пробел”, +,^ — разделители. Чем обычный символ отличается от разделителя? Разделитель – всегда отделяет одну лексему от другой. Наш оператор присвоения, будучи разбит на лексемы, выглядит так: “x”, “=”, ”15”, “y”, “^”, “2”.
Как можно заметить, не все разделители попадают в результат парсинга (пробелы, в частности, не попадают). Будем называть разделители, которые не попадают в результат, разделителями первого типа. Остальные разделители будут называться разделителями второго типа.
Входом парсера будет строка, выходом – список строковых лексем. В качестве накопителя будет использоваться локальная переменная – аккумулятор. Первоначально аккумулятор содержит пустую строку.
Алгоритм парсинга может быть таким: читаем символ за символом входную строку. Если встретился обычный символ, конкатенируем его с аккумулятором. Если встречается разделитель, то:
- Для разделителя первого типа сбрасываем значение аккумулятора (если он непуст) в выходной список, очищаем аккумулятор и переходим к чтению следующего символа;
- Для разделителя второго типа также сбрасываем в выходной список значение непустого аккумулятора, а вслед за ним заносим в выходной список принятый разделитель второго типа (как самостоятельную лексему), очищаем аккумулятор и переходим к чтению следующего символа.
Вот код парсера:
(defun parser (txt &optional (d1 " ,") (d2 "()+-*/^=<>%"))
(let ((res nil)
(lex "") )
(iter (for s in-string (strCat txt (strLeft d1 1)))
(cond ((plusp (strInd d1 s))
(when (> (strLen lex) 0) (collecting lex into res))
(setq lex ""))
((plusp (strInd d2 s))
(when (> (strLen lex) 0) (collecting lex into res))
(collecting s into res)
(setq lex ""))
(t (setq lex (strCat lex s))))) res))
У функции кроме обязательного параметра имеется два необязательных: d1 содержит строку, каждый символ которой есть разделитель первого типа, а строка d2 содержит разделители второго типа.
Программная логика функции parser описана выше. Следует только отметить, что перед началом работы к концу входной строки присоединяется разделитель. Это сделано для того, чтобы последняя обработанная лексема на “зависла” в аккумуляторе (роль аккумулятора играет локальная переменная lex).
Проверим наш парсер “в деле”:
(parser "x = 15 + y^2")
==> ("x" "=" "15" "+" "y" "^" "2")
Все верно, не так ли? Но работать со списками строк – это не вполне по-лисповски. Давайте от списков строк перейдем к списку атомов. Для этого нам понадобится функция, которая… склеит все лексемы опять в длинную строку (но между лексемами вставит по пробелу), потом к началу этой строки приклеит открывающую скобку, к концу – закрывающую… а затем заставит Лисп прочитать полученный список:
(defun mk-intf (txt)
(let ((lex (parser txt " ," "()+-*/^=<>%"))
(intf ""))
(iter (for a in lex) (setq intf (strCat intf a " ")))
(input (strCat "(" intf ")"))))
Теперь, если подать на вход функции mk-intf наш оператор присвоения, то получим:
(mk-intf "x = 15 + y^2")
==> (X = 15 + Y ^ 2)
Что, согласитесь, гораздо приятнее.
Теперь немного изменим функцию start: эта функция должна будет читать и обрабатывать целые процедуры:
(defun start (&optional (fname nil))
(setq *numline* 0)
(setq *flagerr* nil)
(when (null fname)
(setq fname (sysGetOpenName (sysHome) "Мини-бэйсик|*.mbs")))
(when fname
(let ((fi (gensym 'fi)))
(filOpen fi fname _INPUT)
(loop
(let ((curr-proc (action-proc fi)))
(when (or *flagerr* (filEOF fi)) (return t))
(eval curr-proc)))
(filClose fi))
(when *flagerr* (printsline "**** Были найдены ошибки")))
(unset '*numline*)
(unset '*flagerr*))
В теле цикла вызывается функция action-proc (обработать процедуру), которая будет формировать тело принятой процедуры уже на Лиспе. Тело процедуры, сохраненное как S-выражение в переменной curr-proc, передается затем на вход eval. И принятая функция “реинкарнируется” в среде Лисп!
Что должна делать action-proc? Эта функция получает в качестве параметра идентификатор открытого файла. Функция читает из файла строку за строкой, пропускает пустые строки и комментарии, остальные строки парсит, переводит в форму списка, и генерирует тело процедуры.
Мы будем постепенно “учить” action-proc генерации. И начнем с того, что научим нашу функцию распознавать начало и конец процедуры. В мини-бэйсике начало процедуры имеет вид:
proc name(p1,p2,p3)
попробуем распарсить такую строку:
(mk-intf "proc name(p1,p2,p3)")
==> (PROC NAME (P1 P2 P3))
Как же должна реагировать на этот ввод функция action-proc? Вполне естественно: убедившись, что голова списка есть атом PROC, нужно взять второй элемент списка как имя функции, а третий элемент – как список параметров. Имя и список параметров следует сохранить в локальных переменных. Когда же прочитывается оператор end_proc, то нужно из имени функции и списка параметров сформировать форму defun с пустым (пока) телом, и вернуть эту форму как результат. Вот как это выглядит:
(defun action-proc (fi)
(let ((stmt nil)
(proc-name nil)
(proc-parm nil))
(loop
(setq stmt (mk-intf (getLine fi)))
(when (null stmt) (return t))
(cond ((eq (car stmt) 'proc)
(setq proc-name (nth 1 stmt))
(setq proc-parm (nth 2 stmt)))
((eq (car stmt) 'end_proc) (return t))
(t (printsline (strCat "**** Оператор "
(output stmt)
" не распознан"))
(setq *flagerr* t))))
`(defun ,proc-name ,proc-parm (quote OK))))
Для окончательного формирования предложения defun используется обратная блокировка. Обратите внимание на то, что в качестве результата сгенерированная процедура будет выдавать атом OK.
Теперь мы можем проверить наш код в действии. Занесем в файл 0000.mbs следующий код:
proc f1(x,y)
end_proc
proc f2(x)
end_proc
Запустим процедуру start, выберем 0000.mbs и увидим на консоли:
0001 proc f1(x,y)
0002 end_proc
0003 proc f2(x)
0004 end_proc
При желании можно убедиться, что Лисп-машине теперь доступны две (пока бесполезные) функции f1 и f2:
(getd 'f1)
==> (EXPR (X Y) (QUOTE OK))
(getd 'f2)
==> (EXPR (X) (QUOTE OK))
Более того! Их уже можно и запустить:
(f1 1 2)
==> OK
(f2 2)
==> OK
Наш транслятор сделал первый вдох…
Ввод, вывод и локальные переменные
А сейчас самое время научить наш новорожденный транслятор обрабатывать операторы input, print и local.
Проще всего обработаем ввод и печать. Оба оператора имеют одинаковую синтаксическую структуру: ключевое слово и переменная. Оператор input x должен превратиться в такую Лисп-форму (setq x (read)). Соответственно, оператор print x превращается в форму (printline x). Для хранения этих форм необходимо в функции action-proc предусмотреть локальную переменную body. В этой переменной будут накапливаться формы, осуществляющие вычисления будущей функции. Дальше все довольно просто:
(defun action-proc (fi)
(let ((stmt nil)
(proc-name nil)
(proc-parm nil)
(loc-var nil)
(body nil))
(loop
(setq stmt (mk-intf (getLine fi)))
(when (null stmt) (return t))
(cond ((eq (car stmt) 'proc)
(setq proc-name (nth 1 stmt))
(setq proc-parm (nth 2 stmt)))
((eq (car stmt) 'end_proc) (return t))
((eq (car stmt) 'print)
(setq body (append body
(list (cons 'printline (cdr stmt))))))
((eq (car stmt) 'input)
(setq body (append body
(list (list 'setq (cadr stmt) (list 'read) )))))
(t (printsline (strCat "**** Оператор "
(output stmt)
" не распознан"))
(setq *flagerr* t))))
`(defun ,proc-name ,proc-parm ,@body)))
Давайте теперь подготовим вот такой исходник на мини-бэйсике:
proc f1(x,y)
print x
print y
end_proc
proc f2(x)
input x
print x
end_proc
и попробуем его протранслировать… У нас появятся две лисповские функции f1 и f2. Посмотрим на их определяющие выражения и убедимся, что они сгенерированы верно:
(getd 'f1)
==> (EXPR (X Y) (PRINTLINE X) (PRINTLINE Y))
(getd 'f2)
==> (EXPR (X) (SETQ X (READ)) (PRINTLINE X))
Можно и вызвать эти функции, и убедиться, что они работают именно так, как и предполагалось. Пусть вас не смущает, что мы вводим значение в переменную-параметр – просто у нас нет пока локальных переменных… Давайте их добавим.
Оператор local может находиться в любом месте процедуры и встречаться более одного раза. Если в процессе обработки процедуры встретился оператор local, то необходимо взять список переменных и сохранить его в локальной переменной. После встречи оператора end_proc необходимо сгенерировать форму let и “заключить в нее” все выполняемые операторы (пока – только input и print). Вот как теперь будет выглядеть action-proc:
(defun action-proc (fi)
(let ((stmt nil)
(proc-name nil)
(proc-parm nil)
(loc-var nil)
(lv nil)
(body nil))
(loop
(setq stmt (mk-intf (getLine fi)))
(when (null stmt) (return t))
(cond ((eq (car stmt) 'proc)
(setq proc-name (nth 1 stmt))
(setq proc-parm (nth 2 stmt)))
((eq (car stmt) 'end_proc) (return t))
((eq (car stmt) 'print)
(setq body (append body
(list (cons 'printline (cdr stmt))))))
((eq (car stmt) 'input)
(setq body (append body
(list (list 'setq (cadr stmt)
(list 'read) )))))
((eq (car stmt) 'local)
(setq loc-var (append loc-var (cdr stmt))))
(t (printsline (strCat "**** Оператор "
(output stmt) " не распознан"))
(setq *flagerr* t))))
(iter (for a in (setof loc-var)) (collecting (list a 0) into lv))
`(defun ,proc-name ,proc-parm (let ,lv ,@body))))
Список локальных переменных накапливается в переменной loc-var. После завершения обработки процедуры из этого списка строится список пар вида (имя 0). При этом нежелательно повторение одинаковых имен… Как его предотвратить? Конечно, можно на каждой обработке оператора local проверять, нет ли повторения имен (если есть – выдавать сообщение об ошибке). Но, мне кажется, лучше просто устранить повторения, что и делает вызов setof. Теперь давайте протранслируем и запустим вот эту программу:
proc f1(x,y)
local a,b,c
print x
print y
input a
print a
end_proc
Убеждаемся, что она работает именно так, как и предполагает алгоритм. Но все самое интересное впереди!
Отсюда можно скачать окончательный вариант того, что мы с вами нашкодили…
Продолжение следует!
|
|
|||
Случайный прохожий
12.10.20 — 10:54 |
Добрый день! |
||
SleepyHead
1 — 12.10.20 — 11:28 |
Посмотри, что склоняет. Если в этой строке есть кавычки, непарные скобки и еще что-то, может попытаться вывести сообщение и на этом зависнуть (но это не точно). Вообще, офигеваю я с этой функции, почему бы не убрать все «неправильные» символы перед анализом, а потом склонять, так и не понял. Пришлось даже вкорячить клиентам расширение, которое это делает, проблемы исчезли. |
||
Случайный прохожий 2 — 12.10.20 — 11:36 |
(1) Я вначале тоже примерно так подумал (грешил на символ конца строки), но перезапуск службы решает на время проблему, значит дело не в строке. |
0 — 01.01.2020 — 18:00 |
С:Предприятие 8.3 (8.3.15.1830) Бухгалтерия предприятия, редакция 3.0 (3.0.75.37) проф при загрузке из банка в бухгалтерию 8.3 выходит ошибка — словосочетание содержит непарные скобки или кавычки, не удается провести выгруженную платежку на выплату зарплаты по зарплатному реестр. Помогите, кто может |
|
1 — 08.11.2021 — 15:59 |
В личку пришлите мне контакты ваши (можно на почту) и файл, который неверно грузиться/негрузиться. А также по-возможности текст ошибки, исправлю
|