# Фокал - часть 3 (пишется)

Аноним Uirh

Типа продолжение к "Фокал снаружи и внутри"

Фокал часть третья ред. 0.20.4 Говорят, Королёв, когда его спросили, как он относится к научной фантастике, ответил, что мол предпочитает фантастику в чертежах. Полностью солидарен! Ну а если в программном коде? Тогда вот это оно и есть - третья часть своего рода фанфика: поскольку я таки взял уже готовый сюжет, сперва по-своему переосмыслил (и это, можно сказать, была часть первая), ну а потом и сочинил продолжение. Но получившаяся дилогия это ни то ни сё - сюжет просто требует дальнейшего развития. Да и изначально заявленные цели пока что так и не достигнуты... Напоминаю содержание предыдущих серий... Частей?... Глав?... Для мультиков вон какие-то "сезоны" придумали... Но у нас, уж коли это фанфик, пусть будет "канон" и "часть первая", "часть вторая"... В некоторо царстве, в компьютерном государстве жил да был алгоритмический язык, Фокалом именуемый. Пожалуй что самый простой, из всех известных. Такой, что проще уже некуда! Само имя которого: "формула-калькулятор", то есть вычислитель по формулам. Слово "калькули" (вычислять) - дословно "гальковать". От "галька" - мелкие камушки. Которые, не сподобившиеся даже к изобретению счет дикари, так по древнегречесскому абаку и перекладывали. И про эти самые счеты узнали только от вернувшихся из плена солдат Наполеона... Был этот Фокал - языком интерпретируемым, потому как и вправду выполнял роль калькулятора в те времена, когда карманных или даже настольных электронных калькуляторов еще и в помине небыло - элементная база не позволяла. Ресурсов у тогдашних ЭВМ было очень мало, и соответственно Фокал был заточен на максимальную их экономию. Что впоследствии сыграло еще раз - в эпоху мини-ЭВМ. У которых ресурсов тоже было кот наплакал: они предназначались вовсе не для вычислений, а для управления оборудованием в "реальном времени". И занимали ту же "экологическую нишу", что и современные микроконтроллеры размером с ноготь мизинца. Вот и у них ресурсов было примерно столько же, хотя и имели габариты хорошего такого ящика. (Так что оборудование, которым они управляли, было на тот момент самое важное: типа реактора - химического или даже атомного...) Это уже несколько потом наконец догадались, что общение с сидящим за терминалом пользователем это тоже задача реального времени, и для этого появился класс ЭВМ "супер-мини", занимающий уже пол комнаты, но за то обслуживающий целый терминал-класс и имеющий ресурсов побольше, чем "большие" ЭВМ предыдущего поколения. А до того ресурсов мини-ЭВМ (в основном оперативной памяти: внешней, типа дисков, барабанов или хотя бы магнитных лент, как правило вообще небыло) едва-едва хватало на самое простое и нетребовательное. Вот на Фокал хватало, а на Бейсик - уже нет. Да, появился Фокал в незапамятные времена, может и по-позже Фортрана, но раньше Бейска. История об этом не то что бы умалчивает, но врёт как сивый мерин: лично мне рассказывали совсем не то, что нынче пишут в Википедии... Впрочем, не важно; важно то, что все упомянутые языки - языки первого поколения. Где из структур данных - только массив, а из структур управления - метки и переходы к ним: условный, безусловный и к подпрограмме (плюс возврат из неё) - в точности как и у "аппаратной" машины и у отображающих машинные коды языков низкого уровня, известных как "ассемблеры". Впрочем, тогда их у нас называли "автокодами". Среди которых числится например автокод машины Эльбрус, который по этому самому "семантическому уровню" будет повыше иных, самых продвинутых тогдашних языков "высокого" уровня... (Впрочем, это уже совсем другая история, так что как ни будь потом.) Ну так с куда более широко известным Бейсиком, Фокал обычно и сравнивают. Хотя как по-мне, Фокал на фоне это сляпанного на скорую руку подмножества Фортрана, как просвечивающая насквозь чашечка китайского фарфора на фоне жестяной кружки из аглицкого паба. Но в целом - да, исходно сопоставимы и по организации и по функциональности. Только Фокал - язык куда более диалоговый; он более лаконичен, гораздо лучше продуман и вообще рассчитан на хорошо подготовленных пользователей, а вовсе не на начинающих, как Бейсик с его сомнительной "опорой на естественный язык". (Больший маразм сыщется разве что в Коболе!) Впрочем, для начального обучения программированию не годятся ни тот ни другой... Надеюсь, Вы уже поняли, что всё это была только присказка. И догадались, что произведение здесь - сам Фокал. А вот это - только его описание. Сам же он выложен на old-dos.ru - в том числе вместе с исходными текстами. Но для понимания нижеизложенного надо проникнуться его духом, осознать исходный сюжет как комплекс идей и возможные пути их развития. Поэтому (увы-увы) сперва приходится хотя бы кратенько излагать всё с азов, не ссылаясь на ранее уже понаписанное. А понаписано уже много... (Впрочем, каждое вот такое описание "с нуля" - повод для очередной ревизии языка. Да и её результат...) Постараюсь в этот раз без излишних подробностей. (Хелп есть.) Поехали! * * * Итак, Фокал - язык диалоговый, строчно ориентированный. В том смысле, что человек-пользователь сидит за терминалом и ведёт с ейным интерпретатором диалог, набирая командные строчки. Набрал, нажал "ввод" - Фокал что-то сделал (и в том числе выдал результат этой своей деятельности на тот же терминал), после чего выдаёт приглашение ко вводу следующей командной строки. Традиционно это символ * (звёздочка). Для сравнения: Бейсик, как я его застал, делает то же самое, печатая "ready" в отдельной строке (что мол готов к приёму следующей команды). Командная строка состоит из "операторов", каждый из которых предписывает интерпретатору одно законченное, элементарное (для данного языка) действие. (А сидящего за терминалом человека приходится называть "пользователем": так уж исторически сложилось - извините!) Этот самый "оператор" в Фокале (да и в Бейсике) обязательно начинается с ключевого слова, указывающего, что надо сделать. И может состоять из него одного, или содержать что-то еще. Например: Quit - выйти из интерпретатора обратно в операционную систему (ОС). Но сначала его, разумеется, надо было запустить. Например написав в командной строке операционнй системы: foc или foc.exe или даже foc.exe aaa Если файл "aaa" есть - попытается загрузить якобы содержащуюся в нём программу. Если нету - заругается... Так что запустим по-новой и продолжим: Write - выдать на терминал всю имеющуюся в памяти программу Go - запустить её выполнение Go 7.3 - запустить со строки вот с таким номером Write 7.3 - распечатать одну только эту строку. Eraze 7.3 - удалить эту строку Eraze - удалить переменные (запущенная программа заведёт их по-новой) Eraze All - удалить вообще всё (обычно перед загрузкой новой программы) Операторов в строке может быть как один так и несколько - сколько поместится. Тогда они разделяются символом ; (точка с запятой), который внутри оператора не встречается. Там, если есть несколько элементов, они разделяются запятыми. А части таких конструкций - точками. Например: Type 1.23 , 4.56 , 7.8 выдаст на терминал вот эти вот три числа... Или: Type %5.4 , 32109876 выдаст 3.211E+07 потому что формат "%5.4" предписал выдать только четыре значащие цифры. (В поле из пяти позиций, где это число заведомо не поместится!) Командная строка сразу выполняется только если у неё в начале нету номера. И тогда она называется "нулевой" или "прямой", а имеющая номер соответственно "косвенной". Пронумерованные строки сохраняются в памяти и составляют собственно программу. Которую можно запустить командой в прямой строке. А вот остановить... Если сама не остановится, например выполнив оператор Quit. Остановить - только по ошибке: если ожидает ввода оператор Ask - написать ему нечто некорректное, а если не ожидает - два раза нажать любую клавишу: в результате произойдёт переполнение буфера клавиатуры (который - всего под один символ). А я в своей реализации еще и сделал реакцию на комбинацию кнопок Ctrl/Ц - например вот для такого случая: 1.1 Type FCHr(-1); Go 1.1 Go Здесь функция посимвольного ввода/вывода FCHr() забирает символы из буфера клавиатуры всяко быстрее, чем я нажимаю кнопочки... При любой ошибке выполнение программы всегда останавливается; каналы ввода и вывода принудительно переключаются на терминал и клавиатуру; и сообщив что именно произошло (типа: ошибка такая-то в строке такой-то) интерпретатор передаёт управление пользователю - ждёт от него очередную командную строку. Надеюсь, Вы уже догадались, что и в этих и во всех следующих примерах все буквы на самом деле могут быть как заглавные так и строчные. Но заглавными здесь - те, без которых не обойтись, а те которые строчные - писать не обязательно. (Немножко синтаксического сахару.) Впрочем, так вроде все делают... Элементарное для Фокала действие это вычислить выражение и как-то использовать его результат. Как именно - как раз и указывается ключевым словом оператора. А выражение указывает, как производить вычисления по формуле. Например: Type 3*(4+5) - выдать результат на терминал Save Y=3*(4+5) - сохранить его в переменной Y, чтобы использовать потом (Обычно пишут не "Save" - сохранить, а "Set" - установить. Типа установить значение переменной такой-то равное вот тому-то. Но здесь это без разницы: важна только первая буква, остальные всё равно игнорируются.) Правила построения выражений - самые обычные, в точности как и в других языках. Или как в школе учили. Разве что там для упрощения записи дозволялось опускать знак операции умножения. Потому что неизвестные величины обозначались всегда одной буквой (для каждой физической величины - всегда одной и той же: V - скорость, a - ускорелие, l - пройденное расстояние, h - высота, m - масса, t - время, T - температура, U - напряжение, I - ток, R - сопротивление... и так далее и тому подобное), а буде их несколько - со всякими штрихами/засечками, или в тяжелых случаях - с подстрочными или надстрочными индексами... А здесь такой фокус не получается: играющие их роль переменные обозначаются СЛОВАМИ, состоящими из букв и цифр. И, да - начинающимися обязательно с буквы. (Потому что слово, начинающееся с цифры - это скорее всего константа.) А индексы приходится писать после такого слова в скобках. Если их несколько - через запятую. Так что Ax+By+C приходится записывать как A*x + B*y + C Математических операций ровно пять: + - * / ^ (последняя - возведение в степень). Скобки - любые: ([{<>}]) но разумеется они должны быть "сбалансированны". Символы < > тоже считаются скобками - "угловыми". Операций сравнения нет: во-первых некоторые из них будут из двух символов (>= <= ...) что нарушает один из фокаловских принципов, а во-вторых ввести их только для одного оператора (как это сделано в Бейсике) - нарушить другой фокаловский принцип, более важный. А если разрешить их везде, то они потянут за собою логический тип данных... (И типы вообще!) Нам такого счастья и даром не надь, и за деньги не надь, и с деньгами не надь! (Ц) Поэтому в операторе условного перехода If - сравнение с нулём (как, кстати, и в Фортране) и переход на три стороны. Да, не очень удобно: сравнивать два числа приходится их друг из дружки вычитанием... (Зато результат - готовое значение в трёхзначной логике с "нейтральным" элементом 0. Или даже в нечеткой. Которые, в отличии от бинарной, не приводят к парадоксам.) * * * Для сравнения: в Бейсике вроде как всё то же самое: единственный тип данных, те же самые пять операций (разве что возведение в степень обозначается **); переменные - только глобальные, как простые так и с одним или двумя индексами; функции - только встроенные; операторы начинаются с ключевого слова; программные строки - нумеруются. И эти номера тоже служат как метками для операторов передачи управления, так и используются командами, обслуживающими саму программу. Например RUN запускающей её выполнение, LIST выдающей пользователю, SAVE сохраняющей на некотором внешнем носителе, или удаляющей ненужные уже строки DELETE... Первое, что лично мне тогда бросилось в глаза: в Бейсике это именно что "команды", отдельные от операторов языка, и допустимые только в "прямой" строке. А вот в Фокале - это тоже операторы (Do, Write, Eraze), вполне уместные и в "косвенных". Так что программа вполне может обслуживать самоё себя. (Шаг к самомодификации!) Впрочем, на предыдущем этапе компьютеризации этих команд в Бейсике еще небыло: программа грузилась с перфокарт в точности так же, как и фортрановская. (О чем, кстати, говорят до сих пор сохранившиеся перфокарточные "рудименты": операторы DATA и READ.) А на следующем - уже небыло: Бейсик сделали в виде "турбо-среды" - программа набиралась и обслуживалась с помощью встроенного в турбо-среду редактора и системы меню. А вот Фокал и изначально был диалоговым, и желает таковым оставаться впредь! ((Лирическое отступление. Так называемый "интерфейс командной строки", который и использует Фокал, это реально круто, что бы нынче о нём глупцы ни думали... Он на порядок мощнее более "современных" и "продвинутых", и при том на два порядка дешевле и проще в реализации. Позволяет "с помощью хилободов управлять гигабайтами" (Ц). Но недостатки - продолжение достоинств: ненаглядно и трудоёмко (по сравнению, например, с тыканьем в иконки мышкою, когда на экране всё наглядно разрисовано): надо набирать командные строчки, знать командный язык, иметь алгоритмическое мышление и держать в голове модель предметной области... Да, сейчас это уровень крутых профессионалов. Хотя, казалось бы - чего же тут особенного?! Чтобы достоинства реализовались, что и позволит мириться с недостатками, командный язык обязательно должен быть языком программирования, по-возможности хорошо продуманным. Как например UNIX`овский sh. Фокалу есть на кого равняться! Конец лирического отступления.)) Следующее принципиальное отличие: в Бейсике номера строк - целые числа, а в Фокале - дробные. В результате бейсиковская программа - сплошной массив строк, а фокаловская распадается на "группы". В каждой из которых удобно разместить "подпрограмму", выполняющую одно законченное сложное действие. Более того, в Фокале группа - это "естественная подпрограмма": получив управление с помощью оператора вызова подпрограмм Do (а не Go или If, которые просто передают управление строке с указанным в них номером) группа, по достижении её конца, вернёт управление в точку вызова (следующему после Do оператору) даже в том случае, если там в ней небыло оператора возврата из подрограммы Ret. Заметим, что "естественной подпрограммой" является так же и каждая отдельная строка, и тело цикла - остаток строки после оператора For. Чтобы вызвать подпрограмму-строку, адрес перехода в операторе Do должен быть дробным числом, а чтобы всю группу - целым. Впрочем, так и во всех остальных операторах, имеющих дело с программными строчками. Если же надо указать всю программу целиком, это делается с помощью ключевого слова Ales (что собственно и значит "всё".) И, кстати: везде, где по смыслу нужен номер строки, может быть вовсе не константа (как в большинстве реализаций всё того же Бейсика), а как и везде, где по смыслу нужно число - выражение произвольной сложности. Получается в том числе "вычисляемый переход"... Пользуясь тем, что каждая строка - тоже естественная подпрограмма, можно при отладке запускать строчки поштучно и после каждой смотреть что она сделала. Если же в ней есть передача управления, то чтобы это отслеживать, есть режим "трассировки" - включается (и выключается) оператором ? (вопросительный знак). При которой выполнение оператора сопровождается выдачей его на терминал. Оператор цикла сконструирован так, чтобы помещался в одной (в том числе "нулевой") строке. Но ежели остатка строки под тело цикла мало - его можно продолжить в другой строке, передав туда управление оператором Go. После её завершения, управление опять вернётся заголовку цикла. (Ну или выделить под это целую группу, передав туда управление оператором Do.) А вот досрочно выйти из цикла с помощью оператора Go, как в других языках - не получится. Для этого надо присвоить параметру цикла значение больше конечного. Или сразу конструировать цикл по-другому: из операторов условной и безусловной передачи управления. (Ну или применить "структурный переход" - вещь из второй части...) Еще одно важное различие с Бейсиком: простая переменная и там и там заводится одинаково: автоматически - при первом её упоминании. А вот переменные с индексами (они же элементы массивов) - уже нет: в Бейсике массив существует весь сразу целиком и для этого должен быть объявлен заранее оператором DIM. При чем индексы в нём не как у всех нормальных людей - с нуля, а почему-то с единицы! (Надо ли это так понимать, что рассчитано на обывателя?) А вот в Фокале ничего и никогда заранее не объявляется. Переменная, что простая, что с индексами одинаково заводится в индивидуальном порядке в момент первого присваивания ей чего либо. Это не эффективно, за то удобно. И сам собою отпадает вопрос о границах массива. Но это таки вызывает некоторые дополнительные сложности: к одной и той же переменной можно обратиться с помощью разного количества индексов, что еще и зависит от реализации. Впрочем гарантируется, что нулевое значение индекса эквивалентно его отсутствию. Так что и Y(0,0) и Y(0) это одна и та же переменная Y. ((Потому что сделано до ужаса просто: под имя и индекс - по два байта. (Маловато? Потом побольше сделаем...) И как туда попали числа - нигде не фиксируется! Так что индексы - не просто целые, а если число большое, оно еще и обрезается... Была еще идея: сделать не только Y[i] или Y[i,j] но и Y.abcdef чтобы первые две буковки тоже туда попадали, а остальные игнорировались. Но вот пока не... Да, это нам не Питон... (Или например Луа.) Где массив это набор пар: ключ-значение. При чем и то и другое могут быть какого угодно типа - полиморфизм, однако! Так что у них там даже переменных как таковых нет. Вместо них - элементы массива: в качестве ключа - строка в роли имени, а значение элемента - хоть число, хоть тоже строка, хоть другой массив, хоть выполняемый код - подпрограмма получается... Но неэффективно. Так что настоящие массивы с пронумерованными элементами у них там вроде бы тоже есть... Но Питон да и Луа это языки третьего поколения, весьма сложные как грамматически, так и в реализации. А Фокал - первого. И очень-очень-очень простой.)) Уничтожить переменные, освободив занятую под них память, можно только все разом - оператором Eraze без аргументов. (Или им же с ключевым словом Ales, но тогда уничтожается вообще всё...) И наконец сами ключевые слова: в Фокале все они подобраны на разные буквы алфавита. В результате чего их можно сокращать до одной только первой буквы. (Остальные всё равно игнорируются.) Чем (вместе с отсутствием "шумовых" слов) и достигается лаконичность Фокала: если в Бейсике в строке помещается максимум три-четыре оператора, то в Фокале - пять-десять. ("Шумовые" слова нужны чтобы операторы могли косить под фразы "естественного" языка. Впрочем, такого же примитивного...) Если в операторе есть второе ключевое слово (вот это самое Ales например), то к нему это тоже относится. А ежели в операторе может быть как ключевое слово, так и число, вернее вычисляющее его выражение, которое тоже может начинаться с этой буквы, то его всегда можно заключить в лишние скобки. Имена переменных - какие угодно, но значимы только первые два символа. Остальные - чисто для красоты, тоже игнорируются. Впрочем, калькулятору этого более чем достаточно. И тоже приучает к лаконичности. Для сравнения: в Бейсике изначально значащим был только один первый символ имени. Имена встроенных функций опознаются по первым уникальным буквам и все обязательно называются на букву Ф. А вот переменные на букву Ф называть нельзя, увы. (Можно, но использовать её потом не получится.) То есть не sin, tg, sqrt, а FSIn, FTG, FSQrt... А чтобы не запоминать, как правильно: например FTAN или FTG - сделано чтобы и так и так. Символы в Фокале делятся на три категории: буквы, цифры и разделители. При чем буквой считается всё что не цифра и не разделитель, которых Фокал знает "в лицо и по фамилии". В том числе буквами считаются: # $ ~_ @ & | \ : Но ведь буквы еще бывают заглавные и строчные, а так же русские и латинские. Не припомню как там было в других реализациях, а я сделал чтобы в именах переменных все они различались, а в ключевых словах и именах функций были эквивалентны. А строку, начинающуюся вот с такой якобы-буквы, повелел считать комментарием. Наряду с начинающейся с оператора Coment (вернее с буквы Ц). Потому что в UNIX`е есть такая полезная традиция: распознавать тип файла не по "расширению" (часть имени после точки), а по "сигнатуре". Каковая - первые два (четыре, или нынче уже бог весть сколько) символов самого этого файла. В том числе, в случае если файл - выполняемый, пользователю, какой на самом деле тип этого файла и как его будет выполнять UNIX - без разницы. А вот для самого UNIX`а... Если файл не в машинных кодах, а текстовый (а уж коли он выполняемый, значит это текст программы на некотором алгоритмическом языке) то первая его строка должна начинаться с символов # ! и содержать командную строку для запуска евонного интерпретатора. А для самого этого интерпретатора - выглядеть как комментарий. Вот и. А остальные такие символы - за компанию. Пробелы, как и во всех других языках, между словами можно вставлять в любых потребных количествах. В том числе и ни одного, если слова разделяются какими-то другими символами-разделителями. Впрочем, в этом случае - если границы слов Фокалу очевидны, то и запятые без надобности. (Что известно как "синтаксический сахар".) Без запятых не обойтись только в случае, когда значим и пропуск элемента. Ну и в выражениях правила более строгие... * * * Таким образом в памяти фокаловского интерпретатора (а значит и в распоряжении пользователя) есть два вида ресурсов: программные строки и переменные. Переменная хранит одно число, любое, а программная строка - тоже любую строку, совсем не обязательно "программную": никто же не проверяет этого! Только когда попытаются это выполнить... Далее: программные строки различаются номерами, а это числа, с которыми Фокал и работает. А вот переменные различаются по именам, а это строки (пусть пока и всего из двух символов). Симметрия проглядывается? Вот только со строками Фокал работать не умеет. Или (пользуясь тем, что строка - это последовательность символов, а символ обозначается коротеньким целым числом) таки умеет, но очень-очень-очень ограниченно... (С помощью спецфункции ввода/вывода FCHR.) Научить Фокал работать со строками было бы очень желательно: пригодилось бы для множества практических целей... Вот только действия со строками совсем не такие как с числами... Первый подход к снаряду (а для Бейсика и единственный) - "полиморфизм". (Оно же "динамическая типизация".) Вот те самые типы данных, от которых я выше шарахнулся, как черт от ладана. Однако в Бейсике именно так и сделали, сотворив из маленького и простого языка (хотя и малость аляповатого и не больно то хорошо продуманного) сложного и громоздкого монстра... Полиморфизм это когда одна и та же переменная может принимать значения разных типов. И одни и те же операции применимы к ним ко всем. (Ну может быть с некоторыми ограничениями?...) В общем я тоже было решил хранить строки, с которыми собираемся работать, в тех же самых переменных что и числа. Проигнорировав (или скорее просто не сообразив), что строки-то у нас уже есть - программные. Впрочем, так делают все и во всех алгоритмических языках... Ну вот и я туда же: придумал, как доопределить пять фокаловских операций для работы со строками. С этого собственно и начался проект модернизации языка "Фокал-3", числа в котором должны были быть не простыми, а гиперкомплексными. Так до сих пор и не реализованный. ("Три" потому что Фокал-2 уже был. И было там много чего, включая "псевдонимы" - чтобы можно было давать подпрограммам "говорящие" имена, подгружать код, не занимающий пространства номеров строк и доступный вот через такое имя; переименовывать встроенные функции, сняв с имён переменных "табу" на букву Ф, а так же передавать подпрограмме параметры "по ссылке" а не только "по значению"... Вот только числа там были обычные - с плавающей запятой...) Но перед этим, в качестве тренировки (да и кое-для каких практических целей) решил сперва немножко развлечься - сделать слегка "доведенную до ума" реализацию традиционного Фокала (назвав это Фокал-1А). Вот до сих пор и развлекаюсь... Идея там была вот какая: строка + строка это их сцепление ("конкатенация"); строка * число, равно как и в обратном порядке (потому как от перестановки сомножителей произведение не меняется) - это размножение строки, дублирование указанное числом количество раз. При чем число может быть и дробное. (И тогда порядок операндов уже имеет значение...) А если отрицательное, то строка инвертируется. Унарный минус - чисто инверсия строки (т.е. буковки в обратном порядке), а унарный плюс - получение длины (т.е. результат - число). (А для комплексного числа унарный плюс это получение комплексно-сопряженного.) Соответстенно деление строки на число это её разрезание. Вернее - взять часть строки с указанного символа, которые нумеруются с первого, чтобы на ноль тоже было делить нельзя. В обратном порядке (т.е. число / строка) можно - результат будет ноль или указанное числом количество символов. Если число отрицательное, то с конца. Вычитание строк это их сравнение: поиск позиции, с которой одна строка входит в другую: числа в операторе If сравниваются именно так! Если вторая строка "больше" - результат будет отрицательный, а если ни одна из них не часть другой - то ноль. Вычитание значения из самого себя это еще и возможность определить его тип: для числа получится ноль, для строки - единица (она же сама в себя входит с первой позиции!), а вот для "пустого" значения результат будет -1. Это самое пустое значение при любых других операциях даёт ошибку, а содержится в неинициализированной переменной, в том числе локальной, если был пропущен параметр соответствующий её позиции. Сложение и вычитание строки и числа я было хотел запретить - сделать чтобы они давали ошибку, но если трактовать число как код символа и соответственно числовое значение как односимвольную строку... Встроенная в Фокал функция FCHr делает именно так, значит можно и тут. Операция ^ и для чисел самая трудозатратная, так пусть такой же будет и для строк: "полукорреляция" - сопоставление двух строк на предмет поиска общей подстроки. Вернее самой длинной из них, буде совпадений окажется несколько. А "полу" потому, что неполная. А то полная корреляция строки с собою найдёт её всю целиком. (А то мы не знаем, что совпадает сама с собою!) Нам вообще-то надо повторяющийся фрагмент: (S/2)^S (здесь мы в первом операнде отрезали от S первый символ - теперь не совпадёт). А коли так уж нужна полная - извольте: fmax(S1^S2,S2^S1). Ну что можно сказать? Всё вроде бы логично, разве что, с одной стороны, слишком много внимания не больно-то реально нужным реверсу строки и её размножению (но это диктуют имеющиеся у нас операции), а с другой - всего этого явно недостаточно: возможных действий со строками куда больше... Например буковки бывают заглавные и строчные, а так же русские и латинские. При сравнении двух строк (в том числе на полное равенство: (S1-S2)*(S2-S1)) в одних случаях все их надо считать разными, а в других - совпадающими. Ну и как? (Ввести некие внутренние признаки эквивалентности, устанавливать и сбрасывать их некоторой спецфункцией? Некрасиво получается...) И это далеко не единственное... Вон в Бейсике ввели для работы со строками кучу функций, "поручив" встроенным операциям только самое очевидное, типа конкатенации. Нам такого щастья и даром не надь... Сложно, громоздко и необозримо даже с помощью встроенной справочной системы. В общем оставались у нас две "неокученные" операции: умножение и деление строки на строку. Вот и решено было, что второй операнд в них - не простая строка, а специальная - составленная по некоторым правилам. Строка1 / строка2 это будет сопоставление с шаблоном, а строка1 * строка3 - "перекодировка". Здесь строка3 это последовательность ПАР шаблонов: включающих анализирующий шаблон и генерирующий. Если первый сопоставился с неким фрагментом строки1, то он заменяется на фрагмент сгенерированный вторым по образчику. Например так можно найти (и выделить) в строке S слово Вася (но только одно): S/"Вася" или даже любое слово на В состоящее из русских буковок: S/"В*%р" - найдётся и Вася и Ваня и Вагонетка (а вот от слова ВАННА - только первая буква В). Или можно (из хулиганских побуждений) заменить все буквы Е на Э (и заглавные и строчные), чтобы получилось как у Великого Комбинатора на пишущей машинке с турецким акцентом: S*"ЕЭеэ" ("Значит, по Вашему я - начальник отдЭлЭния? Свинья Вы, Шура после этого!"(Ц)) И вот тут - на общем случае генерирующих шаблонов, мы и приплыли! Нет, с простыми частными случаями (например замена всех заглавных букв на строчные: S*"%Р%р%Л%л" и русских и латинских) более-менее всё понятно. Но нам же хочется самый общий! А в общем случае может быть что угодно. В частности оные шаблоны (они же регулярные выражения) с помощью двух видов скобочек: () обозначающих последовательность и [] - выбор, могут вкладываться друг в дружку на любую глубину, в точности так же как выражения арифметические. (Конструкция (...) срабатывает если последовательно сопоставились все находящиеся внутри шаблоны, а [...] - если хотя бы один.) И всё это еще и с учетом префиксов повторения.) Ну и что с этим делать? В смысле - как реализовать? Чтобы прояснить этот вопрос, книжку пришлось писать. ("Фокал снаружи и внутри".) По принципу: не знаешь сам - объясни другому! Глядишь, и у тебя самого в голове малость прояснится. И таки да - помогло: с такого разбега, хотя и не с первой попытки, но проблему генерирующих шаблонов я таки забодал... * * * А про ёлку-то ты не забыл, боярин дубовый?! (Ц) У нас же типа пересказ, что там было в первой серии. А даже "канон" еще не весь изложил! А что осталось? Ну буквально же один аспект - про функцию, определяемую пользователем. И, да - еще хотя бы перечислить операторы и функции: Comment - коментарий: ничего не делает, игнорируется всё до конца строки собственно вычисления Set переменная = выражение; - присваивает значение выражения переменной Xecut выражение; - только вычисляет, результат - теряется (Надо бы eXecut - "выполнить", да буква Е занята под более важный Eraze.) ввод/вывод Аск список ввода; - ввод Type список вывода; - вывод Operate устройство; - переключение каналов ввода/вывода обслуживание программы Write номер_строки_или_группы; - вывод текста программы Eraze номер_строки_или_группы; - удаление Modify номер_строки_или_группы; - исправление (чтобы заново не вводить) управление Go номер_строки_или_группы; - безусловная передача управления If (условие) N1, N2, N3; - условная передача управления Do номер_строки_или_группы; - передача управления подпрограмме Ret - возврат из подпрограммы Quit - прекращение выполнения For переменная = нач, кон, шаг; тело цикла до конца строки - цикл дополнительно ? и ?? - включение/выключение трассировки и пошагового режима Break - установить реакцию на внешнее событие Kill - устроить такое событие, или хотя бы системный сброс Vizual - нечто графическое ! командная строка операционной системы - обращение к ОС Help ключевое_слово - справка а так же Load - подгрузить дополнительные функции (до сих пор не реализовано) Provide - выделить место под очень большой массив (тоже не реализовано) Job номер_строки_или_группы; - запустить впараллель (только планируется) Да, это смесь того что сейчас есть, раньше было и возможно еще будет... А функции... математические: FSQrt FExp FLOg - корень, Е в степени Х, логарифм FSIn FCOS FTAn - тригонометрические FASin FACos FATan - обратные к ним преобразования чисел: FABs FSGn - модуль, знак FItr FMOd - целая и дробная часть числа специальные: FRnd - генератор случайных чисел FCHr - посимвольный ввод/вывод FCLk - отсчет временных интервалов FX - обращение к управляющим регистрам внешних устройств FSBr - обращение к подпрограмме как к функции Все подпрограммы что в Фокале что в Бейскике - "процедуры". Параметры, если они им нужны, получают через глобальные переменные. (А они - все глобальные.) Через них же, если что, могут вернуть результат. Но есть ситуации, когда необходима именно функция, которую можно было бы вызвать из середины выражения. Функция как раз и отличается от процедуры тем, что возвращает результат так, что его можно сразу использовать для дальнейшего вычисления этого выражения. Да и параметры (они же аргументы) в этом случае должны быть подвыражениями того же выражения. И в Фокале и в Бейсике для этого есть "функция, определяемая пользователем". В Бейсике она объявляется заранее с помощью оператора DEF и вызывается с помощью встроенной функции FNх где "х" первая буква имени, указанного при объявлении. (Напоминаю: в те поры и переменные в Бейсике тоже распознавались только по одной первой букве их имени.) В Фокале для тех же целей - функция FSUbroutine (что значит "подпрограмма"), но заранее ничего не объявляется: первый аргумент FSUb указывает куда передать управление, а второй передаётся в качестве параметра - присваивается спецпеременной с именем & которая может быть только одна, увы. Но я в своей реализации разрешил передавать не один, а любое, произвольное количество параметров. Которые становятся начальными значениями по-настоящему локальных переменных с именами &0 (она же просто &), &1, &2, &3... К которым к тому же можно обращаться как к одномерному массиву &[i]... И к тем же самым переменным, равняясь на UNIX`овский sh, разрешил обращаться: $0, $1, $2... $[i] (а на самом деле далеко не сразу вспомнил, как же в Фокале называется эта чертова переменная - параметр подпрограммы). Самое интересное здесь - как возвращается результат? Самое логичное, если бы в качестве результата FSBr возвращалось то значение, которое вычислено в операторе возврата из подпрограммы Ret. (Все и всегда так делают!) Ан, фокаловскому оператору Ret выражение не положено. (Хотя я у себя разрешил.) Более того - этого самого Ret может и не быть: "естественная подпрограмма" вернёт управление и так - по достижении её конца... Оказалось, что результатом FSBr становится значение последнего выражения, вычисленного перед возвратом из вызванной ею подпрограммы. Вычисленное в любом операторе, в том числе Xecut, который результат вроде как теряет... Получается, что где-то в недрах Фокала есть скрытая переменная (назовём её "аккумулятором" или А1), куда попадает результат любого выражения. Вот что там оказалось на момент возврата, то и возвращается! Намотаем это на ус: оказывается, Фокал страдает аккумуляторностью! А1 - первый из обнаруженных аккумуляторов, но не единственный: потом были еще найдены А2 и А3. Если усов пока нет - можно приклеить или даже нарисовать. А что касается глобальности/локальности переменной &, то она значима только если из подпрограммы, вызванной с помощью FSBr мы вызовем еще такую же - тоже с передачей ей параметра. Например пишем в командной строке: 1.1 t &; x fsbr(1.2, 222222); t & ! 1.2 t & x fsbr(1.1,111111); Что будет напечатано? Если (как у меня): 111111 222222 111111 то переменная & по-настоящему локальная, а если: 111111 222222 222222 то нет. И тогда фигу нам с маком, а не рекурсия... Рекурсия, это когда подпрограмма вызывает самоё себя, прямо или косвенно. Полезная штука, совершенно необходимая для обхода дерева. Не путешествия вокруг берёзы или например сосны, а что-то типа разбора арифметического выражения: дерево здесь это граф без циклов, каковым это выражение удобно представляется: операции - вершины (узлы), связи их с операндами - веточки, а сам такой операнд - либо листик, если это константа или переменная, либо тоже узел, если это подвыражение, заключенные в скобочки. Или обращение к функции с параметрами, для вычисления которых тоже задействованы подвыражения. Самая первая вершина, из которой и растёт дерево, принято называть "корнем". Вот например для: 1+2+3 или 1+(2+3) или (a+1)*(b-2)/c^3 или -sin(x)+cos(2*x) + + / + дерево: ┌┴┐ ┌┴┐ ┌──┴──┐ ┌──┴──┐ + 3 1 + * ^ - cos ┌┴┐ ┌┴┐ ┌─┴─┐ ┌┴┐ │ │ 1 2 2 3 + - c 3 sin * ┌┴┐ ┌┴┐ │ ┌┴┐ Ха, все деревья - корнями вверх! a 1 b 2 x 2 x Соорудить нечто подобное можно и здесь, в Фокале. Да, ни структур ни ссылок нету... (Вершины обычно изображаются структурами, а веточки - ссылками из них друг на друга...) Ну и что? Массивы-то у нас здесь - динамические! Ну для разбора выражения надо со строками и/или отдельными символами работать... (Тоже можно, но пока сложно и громоздко получится.) А вот пусть нам вводят числа, а мы должны их запоминать (это запросто: массивы то - динамические), а вот как все ввели - мы должны выдать их в отсортированном порядке! Вот такая задачка. Основная проблема здесь - узнать, что числа кончились. А вот пусть среди них не может быть числа ноль. Вернее ноль - обязательно последний. Да, можно тупо запоминать числа в элементы массива, а потом, перед выдачей, просто отсортировать его. Ну хотя бы методом "пузырька"... 1.1 T "вводите числа, 0 - последнее"!; S N=-1; 1.2 S N=N+1; A x[N]; If(x[N]) 1.2, 1.3, 1.2 1.3 If(N) 1.4,1.4; D 2; D 3; T !"Всё."!; Q 1.4 t "Чего же сразу-то ноль? Сортировать нечего!"!; Q 2.1 S j=0; C сортировка методом пузырька - самая тривиальная... 2.2 S j=j+1; S k=0; F i=0,N-j,1; S y=x[i]; If(x[i+1]-y) 2.7; 2.3 If(-k) 2.2; R; C k - признак, что хоть одну пару переставили местами 2.7 S x[i]=x[i+1]; S x[i+1]=y; S k=k+1; C вот как раз это обмен такой пары 3.1 F i=0,N; T x[i]; C тупо выдаём содержимое массива как есть ...Но мы будем сортировать их сразу в процессе получения: строить бинарное дерево. Корень - всегда нулевой элемент массива (то есть самое первое полученное число); m[] и b[] указывают на поддеревья, где все числа меньше и больше вот этого, а -1 там означает что таковых пока нет. (Ибо номера элементов массива - с нуля.) 1.1 T "вводите числа, 0 - последнее"!; S N=-1; 1.2 A y; D 2; If(y) 1.2, 1.3, 1.2; C помещение в массив вынесли в группу 2 1.3 If(N) 1.4,1.4; X FSBr(3,0); T !"Всё."!; Q 1.4 t "Чего же сразу-то ноль? Вот даже выдавать нечего..."!; Q C не только x[]=y но и ссылки на поддеревья устанавливаем... 2.1 S N=N+1; S x[N]=y; S m[N]=-1; S b[N]=-1; S i=0; If(-N)2.2; R; 2.2 S z=x[i]-y; S j=b[i]; I(z) 2.3; S j=m[i] C то же самое на Си: j = (y>=x[i]) ? b[i] : m[i]; по-моему куда понятней! 2.3 If(j) 2.4; S i=j; G 2.2; C спуск по дереву линейный - можно без рекурсии 2.4 If(z) 2.5; S m[i]=N; R; C очередное число повесим как "листик" 2.5 S b[i]=N; R; C Вот он - обход дерева с помощью рекурсии. Почти в одну строчку... 3.1 If(&) 3.2; X FSBr(3,m[&]); T x[&]; X FSBr(3,b[&]); 3.2 R Надо ли тут чего разжевывать, или и так всё понятно? Ну разве что - что квадратные скобки при обращении к массиву - выпендрёж чистой воды: это не Си, можно любые... Впрочем, и построение бинарного дерева вместо сортировки вот для такой задачки - тоже выпендрёж. А вот его обход (уж коли у нас дерево) - нет: он возможен только рекурсией. (Но штука таки дорогая - стэк жрет неумеренно...) Но обычно в качестве примера на рекурсию все и всегда приводят вычисление факториала: произведения всех чисел от 1 до N (что обычно обозначается как N!) потому что видите-ли: (N+1)! = (N+1)*N! Они что - опухли?! Здесь дерево вырождается в линейную последовательность, для обхода которой достаточно обычного цикла. Однако вместо того, чтобы написать: s X=1; f i=2,N,1; s X=X*i; C и всё: X==N! - осталось только напечатать t N,"! = ",X и поискать другой, более содержательный пример, сооружают нечто типа: 4.1 if(1-&)4.2; x 1; r 4.2 x &*fsbr(4,&-1); r t N,"! = ",fsbr(4,N) Как ни странно, это будет работать, даже если переменная & не локальная. А вот если бы написали не &*fsbr(...) а fsbr(...)*& - вот тогда нет! Понятно, почему? Разумеется, у меня локальные переменные тоже создаются автоматически в момент первого присваивания. Но уничтожаются при выходе из подпрограммы. При этом становится доступным предыдущий комплект, если он был. Кроме того, если данная локальная переменная не существует - это не ошибка, как для глобальной: она ищется в предыдущих комплектах, а если так и не найдена - то будет ноль. * * * И вот теперь, когда с "каноном" - "базовым", исходным Фокалом, надеюсь, всё более-менее понятно, краткое содержание первой части сего опуса. (Напоминаю: опус это сам Фокал-1А.) Всё вышеописанное я либо просто воспроизвёл (сохранив и то, что обычно считают недостатками: всего два значащих символа в имени переменной и "вето" на букву Ф), либо немножко модернизировал. Прежде всего уточнил и дополнил концепцию ввода/вывода, в том числе в части взаимодействия с файловой системой ОС. Ну и так, кое-что по-мелочи - разные удобства и красивости: - оператор Help для получения справок; просмотрщик к нему (аналог UNIX`овского more) чтобы их с удобством просматривать; "экранный" редактор командной строки со всяческими прибамбасами, чтобы с удобствами её набирать. Или править с помощью оператора Modify. Работающего поэтому только и исключительно с терминалом, независимо от того куда направлены каналы ввода и вывода. - вот эти самые по-настоящему локальные переменные - как позиционные параметры при вызове подпрограммы с помощью FSUbr, так и сами по себе. И плюс к ним - произвольное количество параметров не только у FSUbr но и у других встроенных функций (де лишние будут просто игнорироваться). А так же возможность пропуска параметра или параметр в виде текстовой константы - для расширения функциональности без увеличения количества этих самых встроенных функций. Например FSQrt(X) - просто квадратный корень, а FSQrt(X,N) - корень N-ой степени; FLOg(X) - логарифм натуральный, а FLOg(X,N) - по основанию N. Или суровая и опасная функция FX( действие, адрес, параметр ) производящая действия с управляющими регистрами внешних устройств (каковых действий всего три: чтение, запись и побитовое-И - например на предмет проверки признака готовности), без первого аргумента: FX( , А, Б) не лезет ни в какие регистры, а всего лишь выполняет побитовое-И между А и Б, рассматривая их как длинные целые: бывает, что надо и в других местах выделять отдельные биты. (Или, используя не вполне корректные фокусы-покусы со знаками А и Б - другие побитовые операции...) Или например так же самая FSBr() без аргументов не вызывая никаких подпрограмм, сразу возвращает содержимое аккумулятора. (Куда иногда могут "зелететь" и такие вещи, как например количество строк, реально выданных на экран просмотрщиком, что больше ну никак...) - парочка-троечка функций с побочными эффектами: FColoro (она же FЦвет) для указания цвета выводимых на экран символов (включая коррекцию палитры); FKурсор (она же FCS) для перемещения по экрану курсора (включая считывание считывание уже выведенных на экран буковок, изменение их цвета, а так же рисование на нём линий и рамочек символами псевдографики); FBIp для издания звуков с помощью встроенного в компьютер динамика (включая ноты и азбуку морзе); FMuso (она же FMышь) для работы с мышкой; FMIn и FMAx (без побочных эффектов) призванные (вместе со сменой знака в роли инверсии) служить логическими операциями И и ИЛИ - в том числе для троичной или нечеткой логики. FTMp для получения текущей даты/времени (в виде количества суток от начала тысячелетия, где время это дробная часть суток) а так же для других действий с датой/временем, включая преобразование в удобочитаемый вид и обратно. (Вот эта вот функция была первой и получилась несколько нестандартной...) - оператор Vizual и парная к нему функция FViz на предмет "управления визуализацией". А на самом деле получения параметров экрана (например высоты и ширины), а так же режима работы дисплея. Включая их переключение, в том числе в один из графических режимов и отрисовку там графичесских примитивов. Однако, эти примитивы оказались слишком примитивными (и пока что этот оператор отправлен на реконструкцию, а функции сией функции - урезаны): Фокал таки калькулятор, и графика ему нужна чтобы можно было рисовать графики - компактно и наглядно отображать результаты вычислений. За сим графическими примитивами должны быть вовсе не "точка", "линия", "дуга окружности", "полигон" и закраска оного, а оси координат (вместе с началом координат, масштабом по каждой оси, в том числе, возможно, нелинейным, и делениями на ней) и уже только после этого точки графика (возможно объединённые в "коллекции"), положения которых на экране автоматически пересчитываютя в соотвтествии с масштабом осей. Эта часть графики - "векторная". А "растровую" предполагается поручить двум функциям FТочка и FЛиния, как это было в Фокале на БК-0010, организованным по аналогии с FKурсор... - некоторая модернизация некоторых операторов: * Вычисление возвращаемого значения прямо в операторе Ret, делающее его аналогом Xecut. (Чисто на будущее: чтобы если и когда будут параллельные процессы, запускаемые оператором Job - чтобы между вычислением возвращаемого значения и возвратом из функции не вклинилось что ни будь, что испортит нам всю малину.) * Передача оператором Do параметров в подпрограмму по аналогии с FSBr * Дополнительные ключевые слова в операторе Write чтобы сообщал дополнительные сведения (в форме пригодной для последующего ввода), в том числе о результатах деятельности операторов Set Oper Do Break... Аналогично и для Eraze - буде там найдётся что очистить. * Несколько выражений в операторе Xecut и несколько присваиваний в операторе Set - выпендрёж чистой воды. (Ибо экономия получается - копеечная.) А вот избавляться от ключевого слова Set (как в Бейсике первое что сделали - сделали чтобы можно было не писать LET) - это фиг: нарушение принципов! Да и экономия в два символа, включая пробел... * Меры по повышению "скриптовости" операторов передачи управления If и Go. Это когда программа не как обычно - сперва грузится в память и только потом запускается на выполнение, а сразу выполняется по мере чтения из файла. Который называют "командным" (а нынче - "скриптовым"). Особенность тут в том, что все строчки командного файла - "прямые". А операторам передачи управления подавай номер строки!. Чтобы от них здесь был какой-то толк, надо бы номера строк как ни будь того... Равняясь на оператор For, у которого тело цикла это остаток строки, вспомним, что и у If есть то же самое - когда последние адреса перехода отсутствуют. Ну так разрешим, чтобы любые, а не только последние - при условии сохранения разделяющих их запятых. И плюс к этому пусть нулевой и отрицательный номер строки тоже что-то значат: нулевой - ничего не делать (перейти к следующей строке), а отрицательный - к следующему оператору, как и пропуск. (А то вызовем FSBr, а она...) И тогда можно например так: If (X) 0, ,0; Type " икс равен нулю "! If (X) ,0 ; Type " икс не равен нулю"! Дополнительно: из трёх выражений в операторе If теперь вычисляется только одно, а остальные - пропускаются. Потому что эти выражения вполне могут давать побочный эффект. И/или в выборе одного из них - смысл всего этого оператора... По аналогии с If - оператор Go 0; передаёт управление на начало текущей строки и таким образом позволяет сделать цикл в одной строчке. Например: Set c=FCHr(-1); type c; If(c-27),0; Go 0; Coment 27 - код нопки ESC Эта програмка в одну строку будет выдавать коды нажатых кнопок, пока пользователь не нажмёт кнопку ESC, она же "эскейп". * Модернизация оператора For на предмет борьбы с плавающей арифметикой: сделано так, что цикл завершается, когда параметр цикла соберётся выскочить за конечное значение более чем на пол шага. (Но после завершения цикла остаётся таким, каким был при последней итерации.) Ну и за одно - автоматический выбор шага, если он опущен: еще и -1 если конечное значение меньше начального. А опустить можно не только шаг, или даже конечное значение (получится аналог оператора присваивания), но и вообще всё. Остаток строки выполнится ровно один раз, но как подпрограмма - что нам тоже понадобится... (Чуть ниже.) - Реакция на "события" и "ситуации" (в т.ч. ошибки). Она устанавливается: Break С = реакция; "Реакция" здесь это номер строки или группы, которую надо запустить, когда произойдёт оное событие или случится ошибка/ситуация. Хотя это может быть и остаток строки: Break С = ; остаток строки... Если С < 0 то это ВНЕШНЕЕ "событие", ловушка на него - глобальная. (Удалить - только явно, указав нулевую реакцию.) Для ДОС`а первые 256 из них реальные прерывания (а под UNIX`ом это будут сигналы); с номерами больше 1000 - свои собственные, типа окончания звукового фрагмента, временного интервала, заказанного функцией FCLk, и.т.п. К сожалению количество таких ловушек очень ограничено - средство это таки экспериментальное. Интерпретатор Фокала - медленный; реакция на событие "вклинивается" между выполнением операторов и выглядит так, как будто между ними вставили оператор Do, но когда это еще будет... Поэтому внешние события только "фиксируются" - наращивается значение связанного с ловушкой счетчика. При реакции оно передаётся первым аргументом, а счетчик - сбрасывается. Принудительно обнулить все эти счетчики: Eraze Break; Если С >= 0 то это ВНУТРЕННЯЯ "ситуация", в том числе ошибка; ловушка на неё - локальная. (Автоматически удаляется при возврате из поставившей её подпрограмы вместе с локальными переменными.) По аналогии с программными строками, события объединяются в группы (ошибки: группы событий 0 - 5), ловушку можно поставить как на всю группу, так и индивидуально. Возникнув, ситуация начинает "распространятся" - последовательно прекращая выполнение вложенных вызовов подпрограмм. И дораспространявшись до интерпретатора, вызывает с его стороны нелицеприятную реакцию. Но буде одна из подпрограмм поставила подходящую ловушку - она срабатывает; запускается подпрограмма реакции, которая якобы и исправляет ошибку. После чего столь предусмотрительная подпрограмма нормально завершается - возвращает управление в точку вызова. Искусственно порождать ситуации поручено оператору Quit (а события - Kill). Например так можно досрочно выскочить из цикла или даже нескольких вложенных: Brek 7; For i=1,N; For j=-M,M; For k=0,L; If(FSBr(ххх, i,j,k))0; Quit 7,"" Вот как подпрограмма по адресу ххх вернёт положительное число, показывая что переданные ей i j k её вполне устраивают, так и. Это и есть "структурный переход". Ситуация 7 выбрана "от балды" (лишь бы не совпадала с уже имеющимися: теми, которые ошибки). Дололнительные числовые аргументы в Quit передаются подпрограмме реакции, но она в этом примере - "нулевая". А вот текстовый - интерпретатору, чтобы он, если что, знал что сказать. А в нулевой строке (как в данном случае) указывает - что мол еще поработаем, что это не команда на завершение работы интерпретатора. Если вышеприведённый пример в одну строку, таки строка подпрограммы, то следующие после неё строки не будут выполнены: ведь тут же случилась СИТУАЦИЯ, но реакция на оную (хотя и нулевая) героически устранила последствия. После чего подпрограмма НОРМАЛЬНО завершается, а не... (Типа: не устрани она - ой что было бы!) Ага-ага. Но если нам надо чтобы выполнились и следующие строки, надо засунуть всё это в персональную подпрограмму, сделав телом оператора For из одного только этого ключевого слова. Вот пусть это она "нормально завершается". - В дополнение к трассировке и пошаговому режиму, которые были всегда, с одной стороны отладочное окошко (правда весьма ограниченной полезности, так как пристойно работает только в текстовых режимах) - чтобы трассировочная информация попадала туда и не на основной экран и не портила изображение; а с другой - микро-хелп в отладочном режиме (подсказка по его командам) и возможность ввести и выполнить командную строку - чтобы например посмотреть значения переменных или заглянуть в основной хелп... А вот перенаправление трассировочной информации в отдельный файл - тот, который указывается в командной строке вторым и открывается под псевдонимом Б, так и не сделано: уж больно как-то оно всё неизящно получается... - Табулостопы и спецкомментарий из одних двоеточий, типа: : : : ! где двоеточиями отмечаются их позиции. Спецкомментарий становится "спец" только если завершается ! (восклицательным знаком) или ; (точкой с запятой), указывающими на конец строки или же её продолжение. Иначе, как и любой комментарий, игнорируется. (Раньше двоеточие тоже числилось буковкой на ряду со всякими # @ $ \ | ~ но поручив ему табуляцию, пришлось назначить его разделителем.) Вобще-то табулостопы понадобились оператору Write со вторым ключевым словом Set, сообщающему результат деятельности оператора присваивания - список переменных, имеющихся на настоящий момент в памяти. Вместе с их значениями, разумеется. Этих переменных может быть как две-три, так и десятки, сотни и даже тысячи, особенно когда мы имеем дело с массивами. И выдавай оператор Write каждую из них в отдельной строке - просматривать всё это было бы муторно. Надо чтобы в виде таблички, желательно стройными рядами и колоннами... Ну так на то и табуляция! Секретарши испокон веку печатали на пишущих машинках всяческие таблички. И у них там эти самые "табулостопы" - такие выдвигающиеся железочки, отмечающие начало очередной колонки. А клавиша ТАБ передвигала каретку к следующему. Но в эпоху алфавитно-цифровых дисплеев их тупо расставили в каждую восьмую позицию... А я сперва сделал чтобы можно было устанавливать какой хочу шаг табуляции, а потом и оные табулостопы индивидуально. И свалил всё это на FVIz. Получилось универсально, но таки сложновато и ненаглядно. А наглядно - в виде вот такого спецкомментария. Он же выдаётся по Write Tab; а Eraze Tab; приводит систему с состояние по-умолчанию. И теперь вот думаю: а ведь запись в базе данных, ну например типа .dbf, она ведь вот так же выглядит? Правда, там у полей есть имена, и их границы пересекать не полагается... А что мешает и мне так сделать? И можно будет заполнять поля оной записи в произвольном порядке, типа: Type :имя, значение; Правда, для этого понадобится еще и буферизация вывода... * * * Подсистема ввода/вывода Фокала (ну наконец-то мы до неё добрались!) устроена очень просто: есть один канал ввода и один канал вывода. Канал вывода обычно направлен на терминал, за которым и сидит пользователь, но может быть переключен оператором Operate куда-то еще. А канал ввода соответственно по-умолчанию подключен к клавиатуре. Из канала ввода читает функция FCHr - побайтно, оператор Ask - построчно, и интерфейсная часть интерпретатора (тоже построчно), когда ей нужна очередная командная строка. Обратим внимание: интерфейсная часть интерпретатора читает командные строки из канала ввода, не интересуясь - к чему именно он подключен. Что и даёт возможность как загрузить ранее сохранённую программу (просто переключив на неё канал ввода - будет вводиться сама собою, пока не кончится...), так и выполнять командные ("скриптовые") файлы - в точности так же. Отличаются только тем, что тут все строки - "прямые", а там - косвенные. То есть ничем: ничто не мешает как добавить после программы прямую строку, которая например её сразу же и запустит, так и командному файлу по мере необходимости подгружать (а потом - удалять) куски программ. (Сильно сомневаюсь, чтобы Бейсику было доступно хоть что-то подобное.) Кстати, при запуске интерпретатора, первый аргумент в командной строке это имя файла, который сразу же открывается (под псевдонимом "А") и на него переключается канал ввода... В канал вывода пишут все, кому не лень: и та же самая функция FCHr - тоже побайтно; и операторы Type, Write, Help; и сам интерпретатор - сообщения об ошибках; и даже функция FTMp. (Прямо не канал, а проходной двор!) Но за то, в отличии, например, от Бейсика, где и для вывода программы "на посмотреть" пользователю, и для сохранения её где-то на внешнем носителе используются разные команды - LIST и SAVE, каждая со своим собственным заковыристым и неочевидным синтаксисом, то в Фокале всё это делает один и тот же оператор Write, синтаксис которого предельно прост. Но конечно для сохранения программы сначала надо переключить канал вывода туда где сохраняем, а потом обратно. А если это файл, то перед этим надо его открыть, а после - закрыть. Но это-то как раз очевидно, хотя и громоздко. Ну так для облегчения жизни (самому себе) при попытке выйти из интерпретатора нажав ESC, выдаётся уже готовая командная строка для сохранения программы в файле "sav.f". (Но только если программа - менялась.) Потому как исправить готовое куда легче чем каждый раз набирать заново. (А при следующем запуске, если явно не указали какой файл открывать, попытается открыть вот этот - тоже под псевдонимом А.) Список вывода оператора Type содержит не только выражения, значения которых и надо преобразовать в удобочитаемый вид, и таинственную конструкцию формат, начинающуюся с символа % (процент), указывающую как именно это делать, но так же еще и текстовые константы, призванные служить пояснениями к выводимым числам. Это любые символы в кавычках, ! (восклицательный знак), предписывающий учинить конец строки, а теперь еще и : (двоеточие), указывающее на переход к следующему табулостопу. Если в выводимом тексте должны быть кавычки - его следует заключить в кавычки другого типа, благо их три: " ' ` Аналогично, список ввода оператора Ask тоже может включать текстовые константы, призванные служить "приглашениями" ко вводу очередного числа. Ну а коли это приглашения, то я сделал что выводятся они только и исключительно на терминал (вне зависимвости от того куда направлен канал вывода) и только в случае когда канал ввода направлен на клавиатуру. Терминал с клавиатурой - вещь особая: там, при выводе задействуется просмотрщик (для Write и Help - автоматически, а для Type - "вручную", с помощью "спецформата" в виде пары символов %), а при построчном вводе - редактор командной строки. А когда он не задействован - ввод попадает в односимвольный буфер клавиатуры - специально для FCHr. А еще у экрана есть курсор, указывающий текущую позицию, и память, где хранится то что на нем нарисовано. (Всё это в ведении функции FKursor.) Но этого может и не быть - терминал может, как в древние времена, оказаться чисто механическим телетайпом, украденным на почте... Файл, впрочем, штука тоже по-своему особенная: от клавиатуры или какой либо линии связи отличается тем, что имеет "текущую позицию". И вообще может быть представлен в виде длинной (но конечной) ленты, разделенной на клеточки, одну из которых "обозревает" головка чтения/записи. Которая при любой операции автоматически сдвигается на следующую. Но может быть принудительно установлена куда нам вздумается. Это логично поручить тому, кто и оперирует с файлами (оператору Operate, разумеется), но вот сообщить текущую позицию он не сможет, для этого нужна функция. Ввёл, и по аналогии с Си-шной назвал FTEll. (Надо было FOper называть! Хотя еще не вечер...) И это была вторая важная вещь, которой и характерна "часть первая". (Потому что всё остальное - удобства и красивости.) А позицию в каком из уже открытых файлов? Решил, что в том, который участвовал в последней по времени операции ввода или вывода. Намотаем на ус: где-то там, в недрах Фокала специально для FTEll завелась скрытая переменная, куда попадает объект любой операции ввода/вывода так же, как в аккумулятор А1 попадает значение (почти) любого вычисленного Фокалом выражения. Забегая вперёд, скажем, что это и есть аккумулятор А3. Оперирует каналами ввода и вывода оператор Operate Кл_слово; где вот это второе ключевое слово и указывает на одно из подключенных к ЭВМ устройств ввода-вывода, или (сейчас) еще и на файл. И тогда называется псевдонимом открытого файла, так как выделяется для него временно - только пока он открыт. Как известно, чтобы добраться до содержимого ящика, сундука, шкафа, потрфеля... ну и файла, разумеется - их сперва надо открыть, а попользовавшись, закрыть обратно. Файлов одновременно можно открыть штук несколько. (Но не очень много: таблица открытых файлов не резиновая). Некоторые ключевые слова испокон веку закреплены за устройствами и использовать их под псевдонимы нежелательно. К терминалу (Tty) и клавиатуре с принтером (Kbd, Lpt) добавились еще I, O, E - стандартный ввод, вывод и вывод ошибок, которые, по традиции UNIX`а открыты у любой программы уже в момент её запуска. (И обычно указывают на те же самые клавиатуру и терминал. Но запросто могут оказаться куда-то перенаправлены...) И плюс к ним ко всем еще и C - стандартный компорт, который ДОС тоже почему-то предоставляет каждому запущенному процессу, нимало не интересуясь, нужен он ему или нет. Файлы открываются (и закрываются): Ope "имя_файла" (позиция) Псевдоним; (закрываются если имя файла - пустое). Позиция (число в скобках) при открытии не обязательна, но если есть - файл будет открыт с неё, а не с начала. Только позиция без имени файла - вот это самое позиционирование. В том числе и фиктивное (если в скобках пусто) - для последующего обращения к FTEll. В отличии от всех других ключевых слов, в псевдониме не все "лишние" буквы игнорируются: некоторые указывают как именно открыть файл (и/или какие каналы переключать): R W A (последняя - тоже на запись, но в конец) и тип файла: B - бинарный, T - текстовый. Если буферизация вывода еще только-только планируется, то на ввод есть два буфера - у оператора Ask и у самого интерпретатора: они оба читают за раз сразу целую строку, а вот используют её - постепенно. Интерпретатор выполняет по одному оператору, а Ask - вводит по одному числу, вернее вычисляющему его выражению... Так, еще раз, и помедленнее: оператор Ask, встретив в своём списке ввода очередную переменную ВЫЧИСЛЯЕТ полученное из канала ввода ВЫРАЖЕНИЕ, а не просто берет оттуда число и преобразует во внутреннее представление, как во всех других языках! Где еще подобное видано?! Фокал - калькулятор, однако... Но вернемся к нашим баранам. Входной буфер интерпретатора позволяет в пределах одной "прямой" строки выделывать с каналом ввода всякие фокусы - это никак не влияет на её выполнение и активно используется, например, для управления порядком выполнения командного ("скриптового") файла. Ведь там, например для обращения к подпрограмме, надо перемотать файл на её начало, сохранив при этом адрес возврата. Если с местоположением подпрограммы - большой вопрос (но пусть мы его откуда-то знаем и уже храним в переменной П1), то с адресом возврата всё просто: текущая строка уже целиком в буфере; указатель чтения/записи - на начале следующей, которой и надо будет вернуть упраление. Поэтому, считая, что командный файл у нас - под псевдонимом А, делаем так: Op () A; Set Ав=Ftell(); Op (П1) A А возврат из такой подпрограммы: . . . . . . . . . . Op (Ав) A И всё. А вот с буфером оператора Ask явно что-то не так... Забегая вперёд, скажем, что эта штука, предназначенная для временного хранения текстовой строки - искомый аккумулятор А2, позволяющий Фокалу работать со строчками. (Вот только делать с ним мы ничего пока не умеем.) * * * И с этого места у нас начинается ЧАСТЬ ВТОРАЯ нашего повествования * * * Не будем тянуть кота за хвост, а сразу возьмём быка за рога: желаем работать с текстовыми строками. Но сами тексты - слишком громоздкие. Пусть так и лежат снаружи в виде текстовых файлов... (Впринципе мы и раньше могли вводить и выводить тексты посимвольно - с помощью спецфункции FCHr, а хранить - в массиве в виде последовательности чисел. Да, стрельба из пушек по воробьям, но на безрыбье и рак - рыба...) Одно из применений - самомодификация. Вот например: мы никак не можем открыть файл, имя которого неизвестно заранее - оно вишь указывается оператору Open текстовой константой! С числами - полный ажур: везде, где по смыслу нужно число, может быть выражение произвольной сложности, которое его и вычислит. А тут?!... Кого - как, а меня такое положение дел совершенно не устраивает!... Но вот появились файлы, доступные на чтение и на запись, и теперь программа сможет сама соорудить себе какой хошь оператор, в том числе вписав там в кавычки всё что ей вздумается... Делаем так: открываем файл (да, с фиксированным именем, но сразу и на запись и на чтение); формируем в нём необходмую нам командную строку, или даже несколько; перематываем этот файл в начало; переключаем на него канал ввода и останавливаем программу! Тогда интерфейсная часть интерпретатора прочитает и выполнит (или сохранит в памяти) то, что мы вот только что написали. Главное: последней строкой заново запустить программу на выполнение. Этот механизм был в Фокале всегда, изначально. Файлов для него подходящих небыло... Да, имя файла тоже было не нужно. Однако, это не единственная константа... Да и самомодификация - это само по себе круто: эффектно, эффективно, смертельно опасно! Даже при предельно простой фокаловской грамматике... Но вот обнаружилась переменная под одну строку. При чем связанная с механизмом ввода/вывода. Впрочем, пока еще только ввода. Да и то... Ask конешно вводит туда строку, но сам же её и использует. Да и операции с ней отсутствуют как класс. Но это пока... Второй подход к снаряду. Начался со скандала: надо было ввести нечто типа: 33А 47Б 11Ц... Нет, число то с помощью Ask N; - это пожалуйста, а вот следующая за ним буковка с помощью Set M=FCHR(-1); - ну никак! Оказывается, оператор Ask захапал в свой входной буфер всю строку целиком, а функция FCHR пыталась прочитать буковку непосредственно из канала ввода, где её уже нет... Ну так это же неправильно! Нехай функция FCHR берет символы непосредственно из канала ввода только когда входной буфер оператора Ask пуст. А если нет - то из него, пока не исчерпается. Ага. В смысле куда конь с копытом, туда и рак с клешнёй! То есть вот щас напридумываем, как не только читать символы из оного входного буфера, но и писать их туда - той же самое FCHr. (С разными видами параметров....) А так же манипулировать указателем, до которого символы в оном буфере уже (якобы) использованы, а после - еще нет. И напридумывали. Правда писать в этот А2 символы получилость только в режиме "замены символов"... Но и это хоть что-то! Однако, осталась еще проблема ввода и вывода. Оператор Ask конешно вводит туда строку, но во-первых только когда полностью использовал предыдущую (или если вдруг буфер был пуст), а во-вторых сам же её и использует. Ну во-первых постановим, что оператор Ask с АБСОЛЮТНО пустым списком ввода - только вводит. А во-вторых поручим ему-же делать этот его буфер пустым: Ask % - перетащим сюда "формат" из оператора Type: там одиночный символ % восстанавливает формат по-умолчанию, вот пусть и тут будет что-то подобное. И, кстати, на будущее: Ask %число пусть вводит не до символа "перевод строки", а указанное оным числом количество символов. Пригодится. Осталась проблема вывода. Миграция формата в оператор Write на предмет просмотра текстовых файлов. А в перспективе - с преобразованием в удобочитаемый вид не очень текстовых... Начиналось всё с того, что надо было выдать из командного файла на экран довольно длинный текст, повествующий о... А, не важно о чем! Что-то типа примитивненькой демонстрационно-обучающей системы. Текст там, естественно, выдавался с помощью операторов Type, будучи помещен у них внутре в виде заключенных в кавычки текстовых констант. На строку - всего по паре символов в начале и в конце: Т".....текст....."! но всё равно набирать муторно и править неудобно... А давайте поручим оператору Write, и без того специализирующемуся на сообщении нам полезной информации, еще и просмотр текстов - копирование строк из канала ввода в канал вывода. Сколько велено, или сколько получится. (Например до конца файла.) Один в один, или с некоторым (указанным форматом) преобразованием - для пущей удобочитаемости... Вообще-то это задумано было и в самом деле для просмотра содержимого текстовых файлов. А формат - на случай если они в другой кодировке: для преобразования текста в ДОС`овскую, с которой только и работает Фокал. Однако, и для вывода текста из командного файла на экран - тоже годится. Но каждый раз считать строчки - неудобно. А пусть у оператора Write % будет еще один аргумент в виде текстовой константы - "метка". Каковую метку он теперь будет искать в каждой копируемой строке. Как нашел - шабаш! А "метку" эту поместим в следующую строку после выводимого текста. Сразу же решается проблема передачи управления в скриптовом файле: есть в операционной системе такое полезное устройство NUL, где безследно исчезает любое количество скопированной туда информации. (И откуда можно прочитать неограниченное количество нулей, только это нам пока не надо.) Найти теперь нужное нам место в файле - элементарно: скопировать всё от начала файла до нужной нам метки на NUL. А строку с меткой на всякий случай сделать комментарием. Но нам то это как поможет? А вот: рассмотрим, как именно копирует Write % строку со входа на выход? Через всё тот же входной буфер оператора Ask! Кстати, строка с меткой в нём и останется. Потом чистить придётся с помощью Ask % А если у нас Write % 1; и вот этот самый А2 не пуст - это и будет вывод его содержимого. А вот если пуст - тогда беда: потащит строку из канала ввода. Поэтому лучше так: Write % ""; (Обратим внимание: метка есть, но пустая. Ввод здесь подавляется, а вывод нет: это специально сделанное исключение.) Итак, подведём итоги: ввод - есть, вывод - есть, с операциями правда жиденько: посимвольное чтение/запись и манипуляция с символами как с числами. Но плюс к этому - использование содержимого А2 там, где допустима текстовая константа: указанием ! вместо "...". Кроме операторов Ask, Type и ф-ии FCHr, где А2 и без того используется, а ! имеет другой смысл. (Глядишь, и самомодефикация не понадобится...) И еще плюс к этому - текстовый "побочный эффект" - тоже в А2. Для тех спецфункций у которых он есть. Например FTEll("шаблон_имени_файла",) ищущая файлы в текущем каталоге. Одна только функция FTMP(t) из этого ряда выбивается - выдаёт текстовую строку (результат преобразования t в удобочитаемый вид) прямо в канал вывода. Но так уж исторически сложилось... Хотя конешно же безобразие! При переключении канала ввода оператором Oper, входной буфер оператора Ask автоматически очищается. Чтобы предотвратить, пришлось добавить в псевдоним буковку N. * * * Третий подход к снаряду: форматные выражения в операторе Write %выражение плюс оператор Modify с присваиванием, который теперь можно бы назвать Move ибо перемещает строчки между группами. Ну да нам без разницы: всё равно значима одна только первая буква ключевого слова. Остальные - игнорируются. Вот оператор Modify номер_строки_или_группы; мы как-то выпустили из виду. Да, в такой форме он всего лишь скромненько позволяет редактировать хранящиеся в памяти программные строки, запуская для каждой из них редактор командной строки. Но позволяет редактировать не только сам текст, но и номер, в результате чего строка в итоге может оказаться в другом месте. (Ибо удаляет и вставляет заново.) А что он будет делать, если его аргумент не число (номер строки) а текст? А то же самое, что и интерфейсная часть интерпретатора! То есть либо поместит этот свой аргумент в память, либо попытается выполнить. Понадобилось это в комплект к пошаговому режиму, где была сделана возможность ввести и выполнить "прямую" строку - дабы можно было посмотреть значения переменных, или даже заглянуть в хелп... Но исправлением и дополнением программы пусть таки занимается тот, кому это положено - вот оператор Modify. А за одно это резкое упрощение самомодификации: всётаки описанная выше технология с формированием строк во внешнем файле и остановкой программы страдает рядом недостатков... (А из А2: Mod !; - оно как-то получше.) Следующий шаг модернизации: Mov N = A; перемещение программной строки, в том числе с добавлением к ней: Mov N = A, B, C... где если первые из A, B, C... - числа то это номера программных строк; а если очередной элемент - "..." или ! (то есть строчного типа), то следующие числа рассматриваются как коды символов. Слева от знака = (равно) в качестве N может быть как число (новый номер строки), так и ! (восклицательный знак), и тогда результат помещается в А2. Таким образом появилась возможность использовать програмные строки как переменные для хранения строчных значений. Пока что возможность - ограниченная: хранимые там строки не должны включать символа с кодом ноль. Но это - вопрос реализации... А преобразование строк... Уж коли Write их копирует, то пусть сам "на лету" что-то с ними и делает. Хотя бы по минимуму: типа вставить фрагмент строки и/или удалить. Ну и найти, куда вставлять и что удалять, тем паче "маркер" для этого у нас уже есть - всё тот же указатель, разделяющий уже якобы использованную часть строки и еще нет. Но пришлось придать ему еще и размер - чтобы указывал что нашли, а не только начало этого... Буковки мы прибережем для грядущих встроенных форматов, а под операции задействуем символы, которые не буква и не цифра - ровно по одной штуке на операцию. Но некоторым нужны уточнения: например для = # ("сохранить" и "применить") - куда именно сохранять и что применять. Так что получается уже два символа. Выполняются операции строго слева направо, без какого либо старшинства, пока выражение не закончится, или до первой ошибки, когда например попытались переместить маркер за пределы строки, или не нашли что искали. (После чего содержимое А2 выдаётся в канал вывода, а на его место читается новая строка из канала ввода. Если конешно вот эта была не последняя.) Да, всё форматное выражение - "одно слово", начинающееся с символа % (процент). И у него внутри не может быть пробела, запятой и тем более точки с запятой, разделяющих слова, части оператора и сами операторы. В роли пробела символ _ (подчеркивание) обозначающий пустую операцию. Основных операций - две: * / вставка и удаление. Способов перемещения маркера тоже два - собственно перемещение на указанное числом количество позиций, и поиск указанного строкой фрагмента. В первом случае размер маркера становится нулевой, а во втором устанавливается по размеру найденного. А в самом начале, когда строка еще только прочитана - маркер ставится в начало и включает её всю. (Потому как решено, что встроенные форматы, когда они наконец появятся, будут действовать не на всё подряд, а только на то, что под маркером.) Два префикса + и - указывают направление перемещения. А операции ^ и $ предписывают поставить маркер в начало и в конец строки. Так: $-10*"Вася" предписывает поставить маркер в конец, отступить на десять позиций и вставить там слово "Вася". Если строка короче десяти символов, то операцию сдвига "-10" выполнить не удастся и слово не будет вставлено. При перемещении, размер маркера обнуляется, но может быть установлен принудительно операцией ~ которой нужен либо правый операнд - число, либо еще какое либо уточнение. Например ~$ (до конца строки) ~^ (до её начала) ~. (до метки). Эта самая "метка" - удобная штука: с помощью =. запоминает текущую позицию маркера, а с помощью #. меняется с ним местами. Префиксы + и - сами по себе предписывающие перемещение вперёд и назад на одну позицию, действуют не только на следующее за ними число, но и на многие операции. Маркер делит строку на три части, условно: A B C - до, под маркером и после маркера (некоторые из которых могут иметь нулевой размер). Ну так -*"ххх" вставит ххх между A и B, +*"ххх" - между B и C, а просто *"ххх" - вместо B. (При нулевом размере фрагмента B это без разницы.) А вот операция / без причитающегося ей числового операнда, который бы и указал, сколько символов удалять, для -/ / и +/ удалит части A, B и C соответственно. Таким образом операции * причитается строчный операнд, / числовой, ~ тоже числовой, а операциям ^ и $ - не причитаются. И префиксы на них тоже не действуют. (Не придумал пока, что бы это значило. Хотя есть идеи...) Разумеется операнд может быть не только константой (это было бы слишком грустно и не универсально), но и значением переменной: #Х (где Х - буква или цифра), и результатом вычисления выражения: {...} и даже #{...}. (Противоположные операции =Х и ={...} тоже очень даже имеют смысл!) Число берется из фокаловской переменной со специфическим именем #Х, которая, впрочем, от других переменных ничем кроме имени не отличается. А вот строка (если Х - цифра, так что лучше писать как #N =N) - из "регистра" форматного механизма. Которых получилось ровно десять штук. И операцией =N туда же помещается: для -=N =N +=N берутся части строки A B и C соответственно. Одновременно в фокаловскую переменную с именем #N пишется размер этого фрагмента. Для -=Х =Х +=Х (где Х - буква) - только размер. Числовое выражение в {...} отличается от обычного только тем, что там не должно быть лишних пробелов, и надо по-аккуратней с как раз и заменяющим пробел символом _ (подчеркивание). А строчное отличается от него тем, что начинается со строчной константы ("..." ! :N) и единственная операция - сцепление входящих туда компонентов ("конкатенация"), которая, впрочем, никак не обозначается - просто перечень компонентов, может быть разделенных подчеркиваниями (чисто для красоты и наглядности). Понадобилась например на случай, когда нам нужны все три кавычки: "'` (там это будет {'"'_"'"_"`"}). Числовые значения, если встретятся, обозначают коды символов. Еще внутри {...} допускается конструкция =Х - присваивание, но не до, как например в операторе Set, а ПОСЛЕ того как вычислено то, что хотим присвоить. #{...} и ={...} - форматные преобразования. (Еще более форматные, чем само форматное выражение, компонентом которого являются, хотя казалось бы что форматней уже некуда.) Формат там внутри - позаимствованный у Си-шных функций printf() и scanf(), да и сами эти конструкции являются их аналогами. (Вот только о преобразовании в код как-то не...) И теперь, когда у нас появились регистры форматного механизма, везде, где по смыслу допустимо "..." может быть не только ! указывающий на А2 но и :цифра указывающая на один из этих регистров. (Но опять же кроме операторов Ask, Type и функции FCHr которые и без того со всем этим работают.) Write Reg; выдаёт содержимое аккумулятора А2 и регистров форматного механизма, а Eraze Reg; очищает их. (Но вроде бы только со второй очереди?) Это была первая очередь форматного механизма оператора Write % Она включает так же префикс повторения @ для организации циклов и круглые скобки для группировки операций: префикс повторения действует только на один элемент - как отдельную операцию, так и вот такую группу. Например: Write %@(+"Васисуалий"*"Вася").... здесь все найденные вхождения слова "Васисуалий" будут заменены на слово "Вася", но остаток форматного выражения, обозначенный здесь как .... выполнен не будет, потому что очереднной поиск слова "Васисуалий" завершится неуспешно и на этом закончится не только цикл, но и выполнение всего форматного выражения. На этот случай есть постфиксы условного выполнения: & \ ? выполняющие (или пропускающие) следующую после них операцию для случаев успешности выполнения предыдущей, неуспешности, а так же ? предусматривающий оба варианта. То есть например, если в: Write %+"Вася"\(^*"###").... фрагмент "Вася" не найден то выполнение форматного выражения (остаток которого обозначен как ....) всё равно продолжится, но дополнительно в начало строки будет вставлено "###". Если подобного нам не надо - после \ можно поставить ничего не делающую "пустую" операцию _ (подчеркивание). Префикс повторения может иметь как числовой операнд, просто указывающий количество повторений, так и заключенный в {} заголовок, аналогичный заголовку оператора For. Префикс повторения с загловком но без тела цикла вместе с постфиксом составляет условное выражение. Кроме того, в первой очереди предусмотрена операция ! для немедленного прекращения выполнения форматного выражения и одновременно подавления вывода строки из А2. (В том числе с префиксами + и - "со скандалом": прекращением работы оператора Write в т.ч. и с ошибкой 3.6 или какой указано), и противоположная к ней операция . (точка) предписывающая немедленный вывод строки из А2 (с префиксами - одной из частей строки). Есть даже обращение к подпрограмме: %Цифра (где цифра указывает один из регистров). Но нету ни одного встроенного формата, для которых и приберегаются буковки, ни "шаблонов" (они же "регулярные выражения") в виде конструкции <...> (Вернее шаблоны-то как раз сперва были, но потом отправились на реконструкцию.) С появлением операции ! устройство NUL более не надобно: Write %! "метка" * * * Шаблоны и некоторые встроенные форматы (те самые, для которых мы и приберегали буковки), в том числе преобразования из одной кодировки в другую, можно считать "второй очередью". Шаблон (он же "регулярное выражение") - конструкция вида <....> так же как и "....", нужен для поиска фрагмента строки (это - "анализирующий" шаблон), ну и конешно как правый операнд операции вставки * (звёздочка), указывающий что именно ей вставлять ("генерирующий"). Шаблон <....> отличается от текстовой константы "...." тем, что в ней каждый символ изображает самого себя (кроме ограничивающих конструкцию кавычек, разумеется), а в шаблоне некоторые из символов имеют специальный смысл. Например . (точка) изображает вовсе не точку а заменяет собою абсолютно любой символ; ^ и $ - изображают начало и конец строки в аккумуляторе, а ! (восклицательный знак) - СИМВОЛ конца строки, что совсем не тоже самое! Да, такой символ УКАЗЫВАЕТ на конец строки; оператор Ask читая из А2 очередную строку, определяет где у неё конец вот по такому символу. .. А состоит он как правило из ДВУХ символов, известных как "перевод строки" (ПС, он же '\n' в языке Си) и "возврат каретки" (ВК, '\r') с кодами 10 и 13. Именно с этой парой (в любом порядке) ! и сопоставляется. Но так же если один из них (тоже любой) - в гордом одиночестве. Соответственно, в конце строки такой "символ" как правило и присутствует. Но совсем не обязательно... Да и вставить его можно посередь строки, разрывая её надвое... Так что <.$.> или <.^.> - заведомо ни с чем не сопоставятся. Потому что ни до начала строки ни после её конца никаких символов быть не может просто по определению, а вот <.!.> - очень даже может. А главный баламут всея Фокала символ % (процент) отбирает у таких вот специальных символов их специальный смысл, заставляя изображать самоё себя. Но и придаёт некоторым "обычным" символам специальный смысл "встроенного шаблона", изображающего собою группу символов. Например: %c - любая цифра, %b - любая буква, %r - любая русская, %l - любая латинская, а вот %л и %Л только заглавная и только строчная. Аналогично для русских букв %р %Р и для букв вообще %б %Б. И кроме того %Г %г %G %g - любой символ псевдографики; %С %с %S %s - строка в кавычках; %П %п %P %p пробел и "слепые" символы (с кодом меньше чем у пробела). При чем для %П %п - плюс знаки препинания, способные завершать слова . , : ; ! ? и конец строки. При чем %П и %P сопоставляются с одним, а %п и %p - с нулём таких символов, то есть просто обнаруживают конец слова. Однако, сила и мощь регулярных выражений - в * ? ("повторение")... Регулярные выражения встречаются... ну не то что бы сплошь и рядом, но по-моему достаточно известны - используются при поиске. Чего либо. Например файлов в каталоге командой операционной системы: DIR имя_файла где вместо имени конкретного файла может быть и вот такой шаблон. Где * (звёздочка), которая в именах файлов не встречается, заменяет собою любое количество любых символов, в том числе и ни одного, а ? (вопросительный знак) - ровно один символ, но тоже любой. (В именах файлов ? разумеется тоже не встречается.) Например под шаблон "*ася" подойдёт и "Вася" и "вася" и "мася" и "ася" и "ЫЫЫася", а под "?ася" только три первые. Следует заметить, что и в ДОС`е и в винде механизм этот сделан крайне халтурно. И как он себя поведёт, когда в шаблоне две звёздочки - одному Аллаху известно. А вот в UNIX`овском ls - всё по честному. И есть еще один элемент: [...] заменяющие собой тоже один символ, но не любой, а один из внутри перечисленных. Или его негативный вариант [~...] - любой из внутри не встречающихся. Внутри могут быть как сами эти символы, так и "диапазон": чтобы не перечислять все с подряд идущими кодами. Например: [a-zA-Z] это все латинские буквы. С русскими буквами такой простой фокус к сожалению не получается. Очень уж от кодировки зависит... Но таки в файловых системах вариант регулярных выражений - крайне упрощенный (можно даже сказать "выхолощенный"). Это как если бы в выражении арифметическом почему-то нельзя было использовать скобки. И функции. Но ведь можно же! А здесь у нас этих скобок - аж два вида: <...> и [...] Кстати, напоминаю: регулярное выражение потому и "шаблон", что "сопоставляется" с фрагментом строки, определяет - подходит он под него или нет. Ну так конструкция <...> сопоставляется когда сопоставились все её элементы, а конструкция [...] - когда один, любой. (Следующие после него даже и не рассматриваются). А элементом может быть любая конструкция, в том числе и вот такая. В результате чего они могут вкладываться друг в дружку как матрешки на любую глубину. Как впрочем и подвыражения в арифметических выражениях... Повторюсь: главная фенька шаблонов - префиксы повторения! Здесь * и ? не сами заменяют какое получится количество символов, а только предписывают сделать это следующему после них элементу. Так что вместо просто звёздочки придётся написать *. а *А соответственно сопоставится с любым количеством букв А идущих подряд - от того места, до куда дошло сопоставление. Но количество повторений может быть и ограничено: {N} или {N,M} - ровно N, или >= N но <= M где N и M - фокаловские выражения. Если отсутствуют то для первого - "от 0", а для второго - "до бесконечности". Так что * это {,} а ? это {,1}. Есть еще + (плюс) эквивалентный {1,} и - (минус) предписывающий данному префиксу не "жадный", а "ленивый" алгоритм сопоставления. Их смысл ясен из названия и проявляется, когда элементов с префиксами повторения в шаблоне несколько... Впрочем, хватит и одного. Например в тексте _____аББаБа___ ленивый *-[Ба]Б выделит только аБ в то время, как жадный *[Ба]Б всё аББаБ целиком. Среди вышеупомянутых встроенных шаблонов есть так же %Д %В %И %К %У изображающие заглавные русские буквы в других кодировках и %д %в %и %к %у изображающие строчные (дД - в ДОС`овской, она же CP-866, вВ - в виндовой, она же CP-1251, иИ - ИСО-8859.5, кК - КОИ-8 а так же уУ - пресловутый уникод.) На предмет преобразования символов между кодировками: типа в анализирующем шаблоне указана одна, а в парном к нему генерирующем - другая. Только русские, потому, что латинские там везде как в ASCII. (Который содран с давнего стандарта ISO. А его похоже спёрли у фашистов: климат - великая вещь! Казалось бы - что такого, подумаешь... А ничего, что в европах даже при ручной обработке почвы один земледелец кроме себя мог прокормить еще одного горожанина, а у нас для этого надо было десять крестьян? И результаты труда вот этих лишних работников накапливаются из года в год, из века в век... Или же с приятностью проедаются... Немцы смогли это грамотно использовать, приняв во всех своих землях еще аж в конце XVIII века, когда и Германии-то еще небыло, законы об обязательном всеобщем школьном образовании. Над которыми долго потешались все остальные европеи. Но лет через сто - к концу XIX века это уже была "страна хорошистов и отличников" - по очень многим пунктам "впереди планеты всей". Вплоть до того, что две науки: мировая (читай англо-саксонская) - отсталая, и немецкая - передовая. А Первую Мировую немцы проиграли (уже практически выигрывая "по очкам") чисто в результате предательства собственной элиты: договорняка вишь им захотелось... Возвращаясь к нашей теме: еще до Второй Мировой у них уже была телетайпетная сеть с автоматической коммутацией, сопоставимая по эффективности с нынешним интернетом. Вот нулевая страница ASCII и заполнена в основом командами управления этой сетью. Ну и телетайпом немножко...) Так что шаблона %L для них всех вполне достаточно. Вот если и когда будет какая либо экзотика, типа собственной ("нативной"?) американской кодировки с неприличным названием ЕБЗДИК или её вариант с русскими буквами ДКОИ, активно использовавшийся в серии ЕС-ЭВМ, сдуру содранной с идиотской IBM-360, что, помнится, еще Дейкстра назвал величайшей победой запада в холодной войне!... Разумеется, в самом форматном выражении преобразования символов между кодировками обозначаются теми же самыми буквами. (И тоже только русскими.) При чем заглавная буква указывает преобразование "на экспорт" - из текущей кодировки в указанную, а строчная - из указанной в текущую. (Которая - всегда ДОС`овская.) Преобразуется тот фрагмент строки, что под маркером. Но префиксы на это тоже действуют. Из других "встроенных форматов" (кроме перекодировок) пока только R r (реверс) и О о (отождествление). Реверс - перестановка в обратном порядке байтов (для заглавной буквы) и битов в байтах (для строчной). О-большая это отождествление русских и латинских букв для одной следующей после неё операции поиска, а о-маленькая - заглавных и строчных. Нашлась так же возможность разрешить синтаксическое недразоумение. Имеющее место при необходимости преобразовать символы из кодировки X в кодировку Y напрямую. (А не сначала в текущую, а потом из неё, что чревато потерей некоторых символов, которые есть и в X и в Y, но в текущей - отсутствуют.) И заключающееся в том что для этого надо две буквы, в то время как "встроенный формат" Х в операторе Write %Х это вообще-то одна... А чтобы две: Write %_%Хy здесь %Х в отличии от просто Х, это типа обращение к подпрограмме. Оказывается, у нас еще и подпрограммы есть! Типа %N или даже %{NNN}, где N - одна цифра - номер регистра, а NNN - выражение, значение которого - номер программной строки. И этой подпрограмме видите-ли тоже аргумент причитается! Ровно один. Вот вторая буковка в %Хy и служит таким аргументом. Но впринципе это может быть что угодно, включая текстовые константы "..." или составные операции (...). Оно не вычисляется а именно что передаётся. И там в подпрограмме оно может быть как вычислено %% так и использовано как текст #%. (Аргумент только один потому, что будь их несколько, это бы потянуло за собой такие проблемы!... Прежде всего синтаксические.) При передаче "..." или (...) теряет ограничивающие его скобки или кавычки. В шаблоне, где %Х - "встроенный шаблон" %N - тоже аналог обращения к подпрограмме. Но уже без параметров - проблемы синтаксиса: оно бы должно выглядеть примерно так же как и в форматном выражении, но здесь для всех других символов главный баламут всея Фокала % работает как "экранирующий". Я было написал, что мол выше имеет место некоторая путанница с заглавными и строчными буквами... Но нет: и Write %_%XY; и Write %_%Xy; и Write %_%xY;... это всегда X -> Y. А вот если: Write %xY; то это две операции: сначала X -> ДОС а потом ДОС -> Y. По другому - действительно устроить путаницу... (Не забываем, что X Y здесь - условности: обе буквы, обозначающие кодировки, обязательно русские.) Однако, проблема преобразования символов разрешением вышеописанного синтаксического недоразумения вовсе не исчерпывается. И включает не только преобразование между кодировками (желательно без потери символов), или, например, попутное превращение строчных букв в заглавные, или латинских в эквивалентные им русские - что частный случай, но и общий, а это может оказаться всё что угодно, что вообще придёт в голову... С частным случаем впринципе должна справиться пара из анализирующего и генерирующего шаблонов. (Для того и придуманы.) Но это "вид снаружи", а вот внутри-то как? Неужто сооружать матрицу преобразований из каждой %X в каждую %Y по полному графу?! (А при добавлении новой?... Слишком громоздко получается!) Можно конешно выбрать промежуточную (или "базовую") %Z и тогда для каждой %X будет нужна только пара: %X -> %Z и %Z -> %X. А все преобразования для общего случая делать вот с этим вот %Z. Частный случай реализуется без проблем, если в качестве базовой кодировки взять достаточно мощную, например уникод, что все обычно и делают. А вот для преобразований он решительно не годится! Хранить в таком виде тексты - еще куда ни шло, но вот обрабатывать... Да и главный фокаловский принцип разумной (и даже минимальной) достаточности там безобразно нарушается! Мало того что оный уникод гиперизбыточен, ну так при этом еще и недостаточен: под сотню разных фасонов стрелочек, звёздочек и прочей чепухи; безсистемно насована куча букв А-латинская с дополнительными элементами; штабеля каких-то непонятных закорючек. Ну и конешно же не просто редкие, а особо редкие китайские иероглифы, известные дай бог паре десятков человек... Но за то нету того, что было в древней семибитной (!) кодировке УПК и заменить это - нечем! А то что этого теперь нет нигде и вот как-то обходимся... Ну так кодировка, поставившая себе целью включать в себя ВСЁ (и зарезервировавшая под это столько позиций, что до сих пор удалось заполнить едва десятую часть) обязана в первую очередь включить в себя ВСЁ, что было в предшествующих ей кодировках, и уж только потом... Не пожелала - под нож её! (А не нарушай собственных принципов!) Но это-то таки еще можно бы простить, а вот безсистемность - нет. Вот те самые рассованные по разным углам буквы A-латинские со всякими дополнительными штрихами и черточками, U и E с разного вида точками, но почему-то не такими же, как у A или I... Но таки люди (при чем далеко не худшие люди!) проделали большую работу, пронумеровав (почти) все возможные символы... А результат их труда нам годится только как справочник - какие вообще символы бывают. И в конечном итоге отправится на свалку. При чем сразу по причинам нескольких уровней. Верхний из которых: она создана с позиций евроцентризма, несёт в себе гнилую идеологию отказа от разума, работает на глобализацию по-американски. Что, впрочем, вполне соответствовало духу времени. Однако, времена УЖЕ меняются - всё это ДОЛЖНО уйти в прошлое. Потому что если не справимся, если не сможем всё это туда "уйти", тогда хана человечеству: вымрет в течении примерно трёх-четырёх-пяти поколений! (Всё перечисленное - симптомы: человечество смертельно больно, но лечить его кроме нас больше некому...) Вот - дожили до "интересных" времён из китайского ругательства, они же - "точка бифуркации". После которой либо глобализация по-русски (через промежуточное состояние "мир панрегионов" обещанное нам Школьниковым), либо отсутствие на Земле человека дважды разумного. А точка бифуркации интересна как раз тем, что "цивилизационная траектория" вдруг начинает зависеть в том числе и от совершенно ранее незначимых факторов... Это когда буквально взмах крыла бабочки способен породить ураган. Или наоборот. За сим, решая свои локальные задачи, удерживаем в виду и вот эти глобальные цели и факторы... В том числе базовую (промежуточную) кодировку конструируем свою собственную - не просто не оглядываясь на, но и... Базовая (промежуточная) кодировка должна быть организована СИСТЕМАТИЧЕСКИ. За образчик можно взять, например, нашу КОИ-8, сделанную как расширение ASCII. Или скорее вот того давнего стандарта ИСО, где остаток страницы после латинского алфавита оставался всё еще незаполненным. Там кодовое пространство делится на страницы равного размера и эквивалентные друг дружке буквы стоят в них на одних и тех же позициях. Поэтому преобразование их друг в дружку осуществляется максимально просто: изменением номера страницы. В том числе между заглавными и строчными - инверсией одного бита. Там, кстати, (в полной версии КОИ-8) есть и псевдографика, и тоже организованная куда более систематически чем в ДОС`овской кодировке, которой приходится пользоваться. Приходится потому что деваться некуда: место под ДОС`овскую псевдографику закреплено аппаратно без возможности его изменения. Но нам всё это не на экран выводить... Так что псевдографику сделаем тоже систематической, как в более приличных кодировках, нежели ДОС`овская. И, кстати, проблема "забоя" (символа из всех единиц) у нас не стоит. Но базовым таки должен быть русский алфавит, как более мощный, а отнюдь не латинский. Который в минимальной комплектации (как у ИСО или в ASCII) никому недостаточен. Кроме тех, для кого он совсем не годится. (Это не парадокс: они пишут сделанными из его букв иероглифами, а алфавитная письменность обязана быть фонетической!) Поэтому всяк, кому его навязали, пытался хоть как-то приспособить вот это для своего языка... (А его именно что навязывали - как один из компонентов гибридной войны, в том числе и на наших глазах и памяти.) И поскольку каждый выёживался как мог, на свой лад - получилось "кто в лес, кто по дрова". Так что "в максимальной комплектации" латиница - жуткий бардак! Попытки хоть как-то это упорядочить, предпринимавшиеся например всё тем же комитетом ИСО, так и не привели ни к чему путному. Так в ИСО-8859 из 16 кодировок 10 (!) - для латиницы. А индийскую "девангари" - (она у них под номером 12) ИСО вообще не потянул. Думаю, что знаю почему: не смогли впихнуть то, что пихали всем, в том числе и в ту, что якобы для нас: ИСО-8859.5 Прикинем: символ - 8-и-битный; 2^8 = 256, или 8 страниц по 32 символа. Кодировок они сделали 16, это еще 4 бита. Надо ли так понимать, что задумана была единая 12-и битная кодировка? Возможно, но вряд ли. Потому что в страницы 0-3 каждой из них, они всем впихнули одну и ту же "священную корову" ASCII; в четвертую (тоже абсолютно для всех) - непонятно для кого предназначенный набор дополнительных управляющих символов. Начинающийся с символа-заполнителя (с кодом 80). При том, что в нулевой странице ASCII уже есть символ-заполнитель с кодом 16. (Передаётся по "синхронной" линии связи в то время пока ничего нет - чтобы не потерять синхронизацию и обозначить наличие несущей. Байт 16 это 00010110 - два импульса: единичной и двойной ширины, дополнительно позволяющий определить младшим или старшим битом вперед передаётся информация. А 80 это 10000000 - один импульс, разве что с краешку. А значит при старт-стопном способе передачи старшим битом вперед сольётся со стартовым импульсом, а вот байт 16 - нет.) То есть для каждой кодировки у них всего три страницы. В первую из которых они неизвестно зачем еще впихнули всем (в том числе и нам) два символа управления переносами: "неразрывный пробел" с кодом A0, запрещающий перенос или другой разрыв строки, как будто соединённые им слова - одно слово. И еще "мягкий дефис" символ нулевой ширины (с кодом AD), наоборот разрешающий перенос вот в этом месте и оказавшись последним в строке, как раз и превращающийся в знак переноса. (И это при том, что у них же во втором комплекте управляющих символов уже есть две команды с примерно таким же смыслом.) Чтобы их впихнуть, русские буквы, без Ё как раз и занимающие всю страницу целиком, сдвинули на пол страницы, нарушив принцип формирования этой кодировки. Но таки в три страницы уложились. А вот с девангари такой фокус у них не получился. Письменность это слоговая, а слогов - много. И хотя заглавных и строчных букв там нет - в уникоде занимает четыре страницы, а не три, как русский с дополнительными буквами для славянских языков. (Чтобы букве Ё не так скушно было...) В общем решено: все остальные алфавиты размещаем в порядке русского, по-возможности по фонетическому принципу. Но страницы по 32 символа маловаты. Следующий размер при двоичной системе счисления - 64. Этого должно хватить и для силлабариев (аналогов алфавита для слоговой письменности). Например в заново переоткрытой Чудиновым русской рунице, символов - штук пятьдесят. (Правда очень любящих объединятся в лигатуры.) Но там для каждой согласной в основном только два варианта - с "передним" и "задним" гласными (смягчающим согласную и не смягчающим) - под тогдашнюю еще весьма нечеткую и грубую артикуляцию. (А все слоги тогда, до нашествия эрбинов, были исключительно открытые.) А вот в индийской девангари хотя и только 47: 33 согласных и 14 гласных, но "согласные" - это сами слоги (парами: обычный и придыхательный, типа ГА-ГХА, КА-КХА...), а гласные - как одиночные, так и модификаторы к ним (чтобы получилось ГО-ГХО, ГЕ-ГХЕ, ГИ-ГХИ...), так что возможно еще столько-же. Это если по-минимуму, а то в уникоде - еще куча лигатур, типа - с разной длительностью... Но уж коли мы таки взялись обозначать не картинки, как в уникоде, а смысл - надо смотреть, как там с сочетаниями согласный-гласный: если все слоги исключительно открытые, то отдельных модификаторов не требуется, а вот если нет... Возможны варианты. Итак, пространство распределяем 64-символьными страницами. (Пусть даже пока что они будут редкозаполненными.) Сдвиг на пол страницы, как это учинил ИСО по отношению к русскому алфавиту - не допускается. Но страница по мере надобности может "складываться" на четыре четвертушки. (Вот как для "нулевой" страницы, где все скобки должны быть друг под дружкою. Или для псевдографики, которой тоже четыре вида.) Или пополам - вот как для алфавитов: под основные символы и под дополнительные (у нас это только Ё), которыми, если что, можно пожертвовать, преобразовав их в основные. Для чего Ё должна стоять в той же позиции что и Е, а не где попало, как у ИСО. (Они её тупо впихнули первой после своего любимого неразрывного пробела A0, и только потом стали подбирать ей компанию...) Итого, рассматриваемая промежуточная кодировка (условно назовём её П-12) содержит по две страницы на алфавит (которых пока только два), страницу под псевдографику и "нулевую" страницу. Она же "системная" - самая главная: должна содержать минимально необходимый, но по-возможности функционально полный набор средств для достижения тех же целей, что преследовались и при построении кодировки ASCII, но с другими приоритетами. Для ASCII главное было ручное управление сетью и оконечным оборудованием. Поскольку вычислительной техники для этого тогда просто небыло. Далее - представление текстов, включее их форматирование - разбиение на слова, строчки и абзацы (параграфы). Для чего и тогда и до сих пор используются некоторые из команд управления оконечным оборудованием. Всё остальное - по остаточному принципу: спасибо что хоть буквы в алфавитном порядке... И цифры. Все прочие символы набросаны как попало... Для П-12 приоритеты обратные. Своим алфавитным порядком, как это сделали в КОИ-8, больше не жертвуем. Управление сетью и оборудованием возлагаем на драйверы... Так: считаем, что в дошедшем до нас потоке символов экранирующие символы (АР1), если они там были - уже изъяты (или при передаче будут автоматически вставлены) и нам без надобности. Так же как и команда "начало пакета" с кодом 01. (Для файла - это его физическое начало.) А вот конец пакета (Ctrl/D обозначающий в UNIX`е конец файла) и конец тела пакета (Ctrl/Ц), а так же его начало - конец заголовка (Ctrl/Б), нам очень даже могут понадобиться. (Но никаких Ctrl/Z как в ДОС`е, ибо это глупость и нарушение протокола! Конец текстового файла приходилось обозначать в те (достаточно древние) времена, вернее в тех файловых системах (ФС) где размер файла только с точностью до блока. В ФС писишного ДОС`а он изначально - с точностью до байта. А что Z последняя буква (ихнего) алфавита, еще и рассчитано на тупорылого обывателя, так же как индексы массивов не с нуля, а с единицы в Бейске...) Сохраним так же три абстрактные команды (тоже из четырёх) оказавшиеся под вышеописанными пакетообразующими символами. (Забегая вперёд: возможно понадобятся для "переключения регистров".) Далее. Именно в нулевую страницу переместим все полезные значки ASCII, не являющиеся буквами. (Их там два раза по шесть штук.) И расположены они тут должны быть систематически. В частности скобки, которых четыре вида - друг под дружкой. И кавычки, которых три. (Сохранять порядок символов что и в ASCII при этом совершенно без надобности.) Под них выделим по два символа в начале двух первых четвертушек и по четыре в конце. Впрочем код 00 остаётся на своём месте: "забой" не перезжает. Остальные команды нулевой страницы ASCII сохраняются. В том числе "вопрос" КТМ ("кто там") и два варианта ответа на него ДА и НЕТ. Правда малополезные ВК и ПФ переезжают на другие места (вместо еще более бесполезных КН и ЗМ). И под вопросом пока остаются два символа: уже упоминавшийся СИН с кодом 16 (который символ-заполнитель - для синхронизации) и АН (анулировать) находящийся точно под ВШ (шаг назад). Символ-заполнитель вроде как в ведении драйвера, а нам нужен управляющий код для образования лигатур. (Это если учесть что постановка диакритических знаков - с помощью ВШ он же \b и символов нулевой страницы. (Условности! Это в оные времена пишущая машинка реально делала шаг назад и печатала второй символ поверх первого...) В общем получается нечто типа: 0 1 2 3 4 5 6 7 8 9 A B C D E F пр - пробел ┌────────────────────────────────────────────────┐ \a == ЗВ ?? == КТМ 0│00 @ /b /c /d ?? ДА \a \b \t \n \v { | } ^ │ \b == ВШ не == НЕТ 1│~ ` к1 к2 к3 не ** \p ** \f \r \E [ \ ] ! │ \n == ПС \r == ВК 2│пр ' * # $ % & . , ; : + ( - ) / │ \p == КБ конец абзаца 3│_ " 0 1 2 3 4 5 6 7 8 9 < = > ? │ \t == ГТ \v == ВТ └────────────────────────────────────────────────┘ \f == ПФ прогон формата \p == КБ конец абзаца (текстового блока) для сравнения - то же самое место в ASCII: си == СИН (заполнитель) 0 1 2 3 4 5 6 7 8 9 A B C D E F АН - анулировать ┌───────────────────────────────────────────────┐ КН - кончилась бумага 0│00 /а /b /c /d ?? ДА \a \b \t \n \v \f \r рс лт│ ЗМ - заменить (цвет шрифт) 1│эк к0 к1 к2 к3 не си \p АН КН ЗМ \E РФ РГ РЗ РЭ│ эк == АР1 (экранирующий) 2│пр ! " # $ % & ' ( ) * + , - . / │ \E == АР2 (ESC) 3│0 1 2 3 4 5 6 7 8 9 : ; < = > ? │ рс лт == РУС ЛАТ └───────────────────────────────────────────────┘ РФ РГ РЗ РЭ - разделители файлов, групп, записей, элементов (для БД) Псевдографика: (по сравнению с ДОС`овской 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F ┌────────────────────────────────────┐ ┌───────────────────────────────────┐ │ │ │ │ 0│ ─ │ ┌ ┬ ┐ ├ ┼ ┤ └ ┴ ┘ & <- * ▀ ░ │ A│ а б в г д е ж з и й к л м н о п │ │ │ │ │ 1│ ■ * ╒ ╤ ╕ ╞ ╪ ╡ ╘ ╧ ╛ & * ▄ ▒ │ B│ ░ ▒ ▓ │ ┤ ╡ ╢ ╖ ╕ ╣ ║ ╗ ╝ ╜ ╛ ┐ │ │ │ │ │ 2│ ∙ * ╓ ╥ ╖ ╟ ╫ ╢ ╙ ╨ ╜ & -> * ▌ ▓ │ C│ └ ┴ ┬ ├ ─ ┼ ╞ ╟ ╚ ╔ ╩ ╦ ╠ ═ ╬ ╧ │ │ │ │ │ 3│ ═ ║ ╔ ╦ ╗ ╠ ╬ ╣ ╚ ╩ ╝ & * ▐ █ │ D│ ╨ ╤ ╥ ╙ ╘ ╒ ╓ ╫ ╪ ┘ ┌ █ ▄ ▌ ▐ ▀ │ │ │ │ │ └────────────────────────────────────┘ └───────────────────────────────────┘ звёздочками здесь обозначены символы разрывающей строчные русские буквы с которыми еще до конца не определились пополам) а & - то что было в УПК и больше нигде нет (диагональные соединители?) Промежуточная кодировка П-12 сразу планируется как двенадцатибитная: ненулевое значение во втором байте - признак что это буквы (а не псевдографика и прочие символы) а так же номер алфавита, которых пока всего два: 4хх русский и 5хх - латинский. И хотя пока не планируется извлекать её из недр Фокала на свет божий даже за пределы оперирующего с целочисленными кодами символов механизма %r (см. далее), однако ничего и не мешает этому... ((Лирическое отступление: немножко про кодировки и их "упаковку" в. Переход на размер машинного слова, кратного не восьми, а двенадцати битам, позволил бы решить массу проблем (см. статью "12 лучше чем 8") и в том числе обеспечил бы равномерной кодировкой все значимые алфавиты и силабарии. (Но для иероглифических письменностей требуется другой принцип.) Сейчас же - при восьмибитном байте, понадобится либо по два байта на символ (т.е 16 бит), либо использование схемы с переключением регистров, как это было в эпоху семибитного байта, либо неравномерное кодирование. Вернее упаковка или расфасовка более длинного числа по восьмибитным байтам. Собственно для переключения регистров и зарезервированы в нулевой странице команды к1-к3. Схема переключения регистров полагается такая: восьмибитный байт это четыре страницы по 64 символа. Нулевая всегда остаётся неизменной, а каждой из оставшися трёх с помощью соответствующей ей команды назначается одна из страниц кодировки. (Нулевая страница туда никогда не назначается и код 00 по прежнему можно использовать для специальных целей.) Команда получается двухбайтная, но это не 64 а 256 страниц. То есть кодировка получается 14-и битная. Даже если зарезервировать первые 8 или 16 страниц для специальных целей, "лишнего" пространства аж в 12 тысяч символов вполне хватит даже на основные иероглифы. (Японцам, например, они нужны далеко не все.) Неравномерное кодирование - ну хотя бы по принципу той же utf-8, где ключевой - старший бит байта. Если он 0, то размер символа 1 байт и это две первых страницы (у них это один-в-один ихнее ASCII, а вот у нас следующей страницей после "нулевой" будет либо псевдографика, либо "системный" алфавит, он же "курсив"); если он 1 то это многобайтная последовательность, состоящая из "ведущего" или "стартового" байта и указанного им количества байт продолжения. У них два старших бита = 10, а остальные это очередные шесть бит полезной информации. (Как раз номер буковки на 64-х символьной странице.) У стартового байта старшие биты это длина символа в "единичной" системе счисления: 110 если 2, 1110 - 3, 11110 - 4... И тогда всего полезной информации будет 7, 11, 16, 21, 26, 31 и 36 бит при 1-7 байтах на символ. Хотя более чем четырёхбайтные последовательности вроде как не используются. Можно сделать ключевыми два старших бита: если они 00 то оставшиеся шесть бит - код символа на нулевой "системной" странице. Если старшие два бита = 10 то это двухбайтный символ - по шесть полезных бит на байт. У байта продолжения два старших бита - 01. И наконец если два старших бита = 11 - то многобайтный: количество дополнительных байтов указывается следующими двумя битами. Общий размер получается от одного до шести байт, итого: 6, 12, 16, 22, 28 и 34 бита полезной информации. Для дальнейшего расширения предполагается зарезервировать "лишнюю" страницу двухбайтной последовательности (с ведущим байтом: 10111111 т.е. BF); проблема забоя решается за счет того, что ведущим байтом из всех единиц - FF придётся пожертвовать. Хотя если ограничиться трёхбайтными символами - 18 полезных битов (что даже уникоду - за глаза: там из миллиона зарезервированных позиций заполнено чуть более ста тысяч, а 2^18 это 256 тысяч) то можно и проще: если два старших бита = 11, то байтов продолжения всегда два. Кстати, это позволяет сделать комбинированный вариант с "переключением регистров": шесть или двенадцать бит номера страницы многобайтного символа запоминаются и для всех следующих байтов продолжения остаются те же самые, а символы со старшими битами 00 их не меняют. А возможные расширения (например переключение кодировок) - за счет "лишней" страницы с ведущим байтом BF. (Это может быть номер кодировки, упакованной вот таким вот образом.) Так пожалуй и сделаем. Хотя это конешно чисто на перспективу, но таки должно получиться раза в полтора компактнее utf-8 - за счет переключения регистров. Разумеется для нас а не для наглов - для них будет как и для всех остальных: никаких преимуществ латинскому алфавиту больше не полагается. И наконец то, что можно условно назвать K-3U, К-3R и К-3П ("К" - потому что комбинированная) различающиеся между собою только умолчаниями, а от вышеописанного - самим фактом их наличия. В K-3U по умолчанию упаковывается уникод, К-3П - П-12 и наконец К-3R - переходная между ними "рациональная" кодировка... Будем различать кодировку и её упаковку. Так 20-и битная кодировка "уникод" упакована в восьмибитные байты пятью (!) разными способами: по четыре байта на символ что известно как utf-32; по два байта на символ плюс для символов за пределами "первой плоскости" (т.е. длиннее 16 бит) - "сурогатные пары"; при чем то и другое - в двух варианта: старшим и младшим байтами вперёд; и неравномерная utf-8 с одним ключевым битом, описанная выше. (А мы здесь в Фокале еще, чисто из вредности, добавили к ним трёхбайтный вариант utf-24.) Соответственно К-3 это способ упаковки РАЗНЫХ кодировок с двумя ключевыми битами - вот как описано выше: комбинированный неравномерно-регистровый. (Да, "регистровый" - это только для потока. Но ничто не мешает помещая текст в память, добавить к каждому ведомому байту причитающиеся ему ведущие, преобразовать в упаковку utf-8, или полностью распаковать, сделав по 2, 3 или 4 байта на символ.) Указать, какая именно кодировка вот так упакована, можно в любой момент с помощью символа на "лишней" странице с ведущим байтом BF. (Хотя я вот сейчас засомневался - правильно ли это? Может назначить "лишней" самую первую страницу с ведущим байтом 80, или даже какую-то в середине... Подумаем.) Но изначально, пока такое указание не поступило - действуют умолчания. Для K-3U упаковывается уникод, вернее первые его 4 "плоскости", которых ему - за глаза. Для К-3П соответственно наша промежуточная П-12, а вот для К-3R то, что описано ниже: - в качестве нулевой страницы - первые 64 символа ASCII - следующие 64 (где латинские буквы и еще 11 полезных символов) - далеко не под первым номером, но включена по-умолчанию. То есть пока не встретилось ни одного не-ASCII-шного символа, от ASCII это ничем не отличается... - остальные страницы - скроены из фрагментов уникода. Но под каждый алфавит или силабарий - целое число 64-символьных страниц. Под алфавиты с заглавными и строчными буквами - по две соседние. Так девангари - один в один. Другие пока что малознакомые нам алфавиты - тоже. А русский - две страницы, где заглавные с кода 410 - с начала первой страницы, а четвертушка с буквой Ё и дополнительными символами для славянских языков, которая с кода 400 - после них. Для строчных проще: с 430 по 45F. - для латинского алфавита - никаких куч букв А с разными черточками. (И никаких дополнительных страниц, где в уникоде весь этот мусор размещается!) Все эти многочисленные символы с дополнительными элементами образуются из основных с помощью ВШ (шаг назад) и одного из символов нулевой страницы. Так например кружочек над буквой указывается звёздочкой, точка - точкой, а умляут (двоеточие) - двоеточием. (А под сопутствующий мусор выделить специальную мусорную страницу из резерва первых страниц, там же где и псевдографика.) Хотя здесь надо иметь в виду способ отображения всех этих символов на экране. Если он графический - то как указано выше. Если же текстовый (то есть выводится только код символа, а в знакогенераторе должно быть энное количество картинок для каждого из таких кодов) - то таки придётся выделить пару страниц под расширения латинского алфавита. (Примечание: что-то не видно, чтобы ИСО, сооружая свой пресловутый стандарт ИСО-8859 заботилось вот о таком отображении символов: нефига было пихать всем дополнительные 32 никому не нужных управляющих кодов - глядишь, для латиницы не 10 кодировок бы понадобилось а одна, ну максимум две.) Упоминавшийся "курсив", он же "системный алфавит" (типа чтобы командовать операционной системой - алфавит по-умолчанию) построенный по "типографскому" принципу, так же как, например, уже упоминавшиеся УПК и ДКОИ: русские буквы и плюс к ним не совпадающие с ними по написанию латинские. Русские буквы в первой половине страницы - как "основные", а все остальные (начиная с буквы Ё) - во второй как "дополнительные". Но по "фонетическому" принципу: В-V а не W! Осталась масса свободных позиций, куда можно поместить буквы "с элементами": эсперантские "со шляпами", немецкие "с умляутами" плюс "гросе-ЭС; некоторые буквы из телеграфной азбуки: N E A-с_кружочком (уж коли они там удостоились отдельных позиций, значит и в самом деле кому-то реально нужны). А если сделать еще и маленькие курсивные буквы, то вместе с нулевой страницей и псевдографикой получится вполне себе самодостаточная восьмибитная кодировка. Но с проблемами. Во-первых дополнительные маленькие буквы не совсем такие-же как заглавные: v d ё z i j k l r s t f h q w А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я V G Ё Z I J L N R S U F Q Y W о гр. ^ ^ : ^ ^ ^ ~ ' : : А Эс G J O H C S N E U A А во-вторых с одной стороны всё равно остаются незанятые позиции (но занять-то их придумаем чем - например некоторыми из букв греческого алфавита), при этом на одну позицию претендуют сразу несколько символов. Позиция буквы Ж в Эсперанто это J-со_шляпой, но Z-со_шляпой было бы логичнее. (Хотя вот как раз это - вопрос шрифта а не кодировки!) И сюда же (вслед за телетайпетной МКТТ-2) обычно относят букву V, а W - на место В-русской, что по-моему неправильно. Вот только приткнуть эту самую лигатуру W получается реально некуда. И в позицию буквы Д так и просится G-со_шляпой, обозначающая звук ДЖ, но тогда некуда будет приткнуть букву Д-маленькая_латинская, таки отличающуся от русской по написанию. По поводу Н-со_шляпой и мелкой латинской h - то же самое. Так что придётся пойти на ряд компромисов: сделать написание Д-строчной русской такое, чтобы её можно было принять и за Д-строчное латинское, а не как рукописное, совпадающее с G-строчной латинской. Соответственно такую букву поместить как парную к Г-маленькой. Парной к Д и большой и маленькой ДЖ в виде G со шляпой. Эсперантскую h-со шляпой, обозначающую непридыхательную Х совпадающую по звучанию с русскою, так и придётся перенести в позицию мягкого знака. Выселив оттуда лигатуру W. Её так уж и быть сохраним - в основном чтобы строчная изображала за одно и букву омега, но перенесём в позицию буквы Ц. А в качестве эсперантского У-краткого придётся использовать У-с_умляутом. (Впрочем, это тоже вопрос шрифта: двоеточие там или черточка.) И получается: | v g | ё | z i j k l | r s t f h w | | q | | | | | А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я | | V G | Ё | Z I J L N | R S U F W | | Q | Y | | | | о гр. ^ ^ : ^ ^ ~ ^ ' : : А эс G J O C S N H E U A Итого остаётся 11 свободных позиций. Куда можно поместить буквы альфа, (где Б-строчное), бета у нас уже есть, гамма (где К-заглавное), дельта, мю, ню (где М и Н-строчное), пи, ро (где буква П), омега-большое (где Х-заглавное), фи (на пару к букве У-строчное) и еще что ни будь (например сигма) на пару к Ы. И еще проблема с преобразованием в другие кодировки, где русские и латинские буквы - раздельные. Впрочем, у ДКОИ и УПК - то же самое. Ну да с ним не детей крестить... И вообще, это пока - так, мысли вслух. Конец лирического отступления.)) Однако, вернемся к нашим баранам: одной только базовой кодировки мало, нужен еще и инструмент для её использования. Который бы что-то делал с символами, рассматривая их как битовые наборы и/или беззнаковые целые. Соответственно "что-то" это операции арифметические и побитовые. И разумеется желательно, чтобы набор этих операций был функционально-полным. Вот для этого и приспособили %r. Реверс - вещь не больно то нужная. Вот и нечего фигнёй страдать - пусть чем-то более полезным занимается. И уж коли ему таки положен аргумент - пусть это и будет операция. А он пусть выполняет её последовательно над каждым байтом фрагмента под маркером... Ага-ага, а ничего, что у большинства операций по два операнда? И что промежуточные результаты, в том числе как символ уникода так и во внутренней кодировке, которую вот только что обсуждали, будет размером более восьми бит? Это я к тому, что для %r нужно место, для хранения вот этих вот операндов и промежуточных результатов. По-моему тут идеально подойдёт примерно такой-же стэк, какой был у советского программируемого микрокалькулятора Б3-34 (и у МК-56). Всего четыре ячейки, но хватало. И тут, думаю, хватит. Так что основная тактика - считать очередной байт на стэк, что-то там с ним сделать и вернуть взад. Или, например, добавить к содержимому регистра... Но это не одна, а несколько операций. А что нам мешает передать %r несколько, заключив их в скобки или в кавычки? Но у нас ведь куда конь с копытом, туда и рак с клешнёй: %R которому тоже делать особо нечего, тоже пожелал во всём этом поучаствовать! Только возиться с каждым из байтов, как %r, ему в лом (слишком вишь мелко, для такого солидного!) - хочет чтобы со всем фрагментом одним махом, рассматривая его как единое число или битовый набор. В результате получилась арифметика неограниченной точности, правда пока только целочисленная. (Но, например, чтобы поэкспериментировать с кодами Рида-Соломона - уже достаточно.) Набор операций и для %r и для %R практически один и тот же: включает арифметические и логические (вернее побитовые) операции и выполняется над числами, снимаемыми с вершины собственного операционного стэка. И туда же помещается результат. Но для %r его ячейки - это просто целые числа, в данный момент 32-х разрядные, а для %R - аналог регистров форматного механизма, содержащих многобайтные значения неопределенной длины. Каждая операция тоже (как правило) обозначается одним символом: , . ; -чтение, запись и обмен с фрагментом строки под маркером [ ] : @ -дублирование, удаление, обмен пары и черырёх ячеек стэка & | ^ ~ -побитовые операции И, ИЛИ, исключающее ИЛИ и инверсия. + - * / - арифметические операции. Деление даёт сразу и частное и остаток. ^ $ % - лентяйская арифметика - без переносов (якобы "с полиномами") Но некоторые - таки двумя символами ( < > = # приходится уточнять): N -циклический сдвиг на 0..9 разрядов (N - одна цифра); << >> -нециклический сдвиг на указанное первым аргументом число битов < <= > >= = <> >< -"предикаты": сравнивают две верхние ячейки стэка #N =N #X =X -(где N - цифра, X - буква) чтение/запись регистра и переменной `А -ввод константы - кода указанного символа (кавычка - любая) А так же и константы в виде числа: NNN 0xNNN 0oNNN 0bNNN -в 10-ой, 16-й, 8-ой и двоичной системе счисления Однако спецсимволов мало, пришлось пустить в ход и буковки. Уже известные: б д в к и у -преобразовать операнд из указанной буквой кодировки в П-12 Б Д В К И У -преобразовать из П-12 обратно в указанную (Б - в текущую) u U -прочитать и записать один символ уникода (впрочем - всё это только и исключительно для %r) Но так же и: r R -сгенерировать и поместить на стэк псевдослучайное число ч х (или 'h') -определить четность и вычислить код хеминга л -получить размер ненулевой части операнда (в байтах) m -создать "маску" из указанного операндом количества единичных битов i -получить индекс (номер) текущего байта в обрабатываемом фрагменте l -получить количество оставшихся байт (то и другое - только для %r) i N -получить и установить "номинальный" размер операнда (только для %R) l L -получить и установить размер операнда (в байтах) (только для %R) А а -запись текущего значения в числовой аккумулятор А1 и чтение из него F f -запись логического результата предыдущей операции в А1 как -1 0 +1 Дело в том, что предикаты, не изменяя операндов, вырабатывают некий логический результат - "статус", как-то действующий на следующую операцию. Впрочем, большинство операций его игнорирует, а сами вырабатывают "нейтральный". Но вот следующие: M S -константы ("младший" и "старший") дают отрицательный и положительный p n c -операции ("позитив", "негатив" и "циклатив") - изменяют ? \ -выполняют или пропускают следующую за ними операцию, (или группу) ( ) -заключают в себе группу операций; для статуса "прозрачны" (не меняют) { } -цикл: { запоминает место; } переход к запомненному. Баланс, в отличии от круглых скобок - не отслеживается: цикл может быть только один. ! -немедленно прекращает выполнение последовательности операций (для %r только для очередного байта и провоцирует переход к следующему; чтобы - совсем, надо совершить серьёзную ошибку, типа деления на ноль) Как видим - имеются даже некоторые средства управления порядком действий. Это не всё - но за кадром осталась еще буквально парочка операций. Логические константы M S происходят от слов "младший" и "старший" потому что влияют еще и на следующее непосредственно за ними обращение к регистру =N и #N организуя "вагонку" - не замену или считывание целиком находящегося в регистре значения, а аналог действий с железнодорожным составом, формируемым путём добавления или удаления вагонов с обоих его концов. Так, а мы еще не забыли за всеми этими разновсякими операциями - о чем вообще шла речь?! А вот о чем: есть у нас в Фокале оператор Write (от слова "врать", откуда кстати и "врун" и "врач") - ну типа скажи-ка нам что ни будь такое-эдакое... И правда специализирующийся на сообщении нам разной всякой полезной информации, начиная от хранящихся в памяти программных строк. При чем в такой форме, чтобы можно было всё это потом обратно ввести... И вот свалили на него еще и из файла строчки выдавать... С некоторым их "на лету" преобразованием - якобы для вящей удобочитаемости. И наобещали наделать для этого встроенных форматов, каждый из которых будет обозначаться одной буковкой. (А то, например, развели кодировок, отличных от ДОС`овской - поди прочитай...) Ага-ага: "обещанного три года ждут"? Если бы только три... Но тем не менее в конце концов... А пока, чтобы сильно не плакал, сделали ему парочку операций (в виде "форматного выражения") типа вставить/удалить кусочек строки. А перед этим выделить... Ну или найти... И вот через некоторое время завелись у нас там "шаблоны" (они же "регулярные выражения") чтобы указать что ищем - не только константой "...", но и неким более общим образом. (Впринципе <...> это тоже типа кавычки, только не "лапки" а "ёлочки"...) А потом, когда наконец появились некоторые из давно обещанных "встроенных форматов", одному из них (%r - "реверс") поручили дополнительную работу - выполнять над кодами символов арифметические операции. А его старший братец %R - сам влез... * * * Ну и как же всё это использовать? А вот (уж коли всё это уже реализовано) давайте нарисуем такие же картинки как выше - для нулевой страницы П-12 и ASCII а так же для псевдографики. Включая рамочки символами псевдографики. Проверим, а в самом ли деле оно вот так, как выше расписано? Если только на экране, то рамочки можно нарисовать функцией FKурсор. Но если потом надо в файл- придётся это с экрана как то считывать. (Ею же!) Однако сперва нарисуем циферки сверху и слева, а в качестве "основного содержимого" ну хотя бы числа - от 0 до 63 (из переменной #k): 4.1 f j=0,15; t %3.2 #k'' %; s #k=#k+1; C одна строка 3.6 s #k=0; f i=0,3; t " "i" "; d 4; t !; C все четыре 3.7 r Второй % в операторе type восстанавливает нам формат по-умолчанию. Верхнюю строчку можно-бы выдать сразу целиком - в готовом виде: 3.2 t !!" 0 1 2 3 4 5 6 7 8 9 A B C D E F"!! Но мы не ищем лёгких путей! 3.2 t !!" "; f i=0,15; w %^__~$__*" "__1__*#{i%x}__. 0 3.3 t !! ; ц 1 2 3 4 5 6 7 Смотрим, что мы сделали. Для наглядности я вставил между отдельными операция по два ничего не делающих _ (подчеркивания), играющего роль пробела. И в следующей строке-комментарии пометил операции цифрами. Итак: 1 - поставили маркер в начало А2 (так как неизвестно, где он был до этого); 2 - сделали, чтобы всё, что сейчас есть в А2, стало "под маркером" - установили его размер до конца строки (мало ли что там в А2 осталось с прошлого раза); 3 - заменили всё это на два пробела; 4 - сдвинули маркер на одну позицию (в результате чего его размер стал нулевой); 5 - вставили туда то, что нам выдала операция 6, преобразующая значение переменной i в шестнадцатиричное число (по формату %x); 7 - выдали что получилось в канал вывода. Теперь нам надо рамочку. Из символов псевдографики. Впринципе не проблема. Можно в готовом виде: 3.3 t ! " ┌────────────────────────────────────────────────┐"! 3.6 s #k=0; f i=0,3; t % " "i" │"; d 4; t "│"!; C добавили две боковые "│" 3.7 t " └────────────────────────────────────────────────┘"!; r Или мы опять не ищем лёгких путей? 3.9 f i=0,15; t "───" 3.3 t ! " ┌"; d 3.9; t"┐"! 3.7 t " └"; d 3.9; t"┘"!; r ;C впрочем, эффект совершенно одинаковый... Этого хватит, или таки продолжим выпендриваться? В смысле рамочку нарисуем на экране с помощью функции FKурс, вообще-то заведующей перемещением маркера, но за одно умеющей и это. Вот только надо знать координаты углов на экране. Хотя бы двух. Сообщить может она-же. И построчно считать, чтобы скопировать с экрана в файл - тоже она... Нет - пока воздержимся. Теперь наконец самое интересное, то, ради чего мы всё это затеяли. Возьмём число из #k (которое, как видим, изменяется от 0 до 63, т.е. в пределах одной 64-х символьной страницы), преобразуем некоторым образом, резуьтат поместим в #b. (Для начала - один в один: Set #b=#k;.) И выдадим. Для непечатного символа (меньше пробела) как число, а для печатного - как есть. (Меж двух пробелов, потому что под символ у нас - три позиции.) 5.1 i(#b-fchr(' ')) 5.2; t ' ';x fchr(#b); t ' '; r 5.2 t %3.2 #b'' ; чтобы попробовать - можно и один в один: 4.1 f j=0,15; s #b=#k; d 5; s #k=#k+1; Но таки хотелось бы чтобы по два символа: не 0 а 00. И шестнадцатиричные. 5.2 t ' '%02.2 #b'' ; C вот это будет по два символа (но пробел - отдельно) 5.2 t ' '; w %^~$*#{#b%02d}. 0; C вот это - должно быть то же самое 5.2 t ' '; w %^~$*#{#b%02x}. 0; C а это - еще и шестнадцатеричное 5.2 t ' '; w %^~$*#{#b%02X}. 0; C а это - еще и чтобы буквы там - заглавные Ага. А захватим-ка то место, где ДОС`овская псевдографика: 4.1 f j=0,15; s #b=#k+fchr("н"); d 5; s #k=#k+1; А теперь то же самое с помощью %r (не зря же переменные #k #b, а не k b). 4.1 f j=0,15; w %^%r"#k`н+=b" 0; d 5; s #k=#k+1; Что видим? 1 2 3 4 5 А вот: 1 - на всякий случай поставили маркер в начало А2 - чисто для того чтобы размер у него был нулевой - тогда %r выполнит переданные ему команды (в кавычках) ровно один раз; 2 - положили на стэк значение переменной #k; 3 - положили на стэк константу - код буквы "н"; 4 - сложили; 5 - записали в переменную #b. И вот теперь, когда наконец всё остальное успешно заработало (не надо обольщаться - тоже не с первого раза: в любой программе найдётся хотя бы одна ошибка, даже вот в такусенькой, как у нас - из пары строк)... Вот сейчас вспомним, какой буковокой обозначается преобразовние из промежуточной кодировки ну хотя бы в досовскую. Кстати русская заглавная Д. Вот и напишем: 4.1 f j=0,15; w %^%r"#k#l+Д=b" 0; d 5; s #k=#k+1; А #l у нас будет номер страницы: мы выведем (в файл) все страницы разом 1.1 F l=0,65; t l!; s #l=l*2^6; d 3 o "стр.txt" xw; o x; d 1; q Что видим? Нулевая страница совершено не такая, как задумано! 0 1 2 3 4 5 6 7 8 9 a b c d e f ┌────────────────────────────────────────────────┐ 0 │ 00 @ 02 03 04 05 06 07 08 09 0A 0B C D E F │ получили 1 │ G ` 12 13 14 15 16 17 18 19 1A 1B c d e f │ 2 │ ! " # $ % & ' * + . , ( - ) / │ 3 │ 0 1 2 3 4 5 6 7 8 9 : ; < = > ? │ └────────────────────────────────────────────────┘ ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? для сравнения ┌────────────────────────────────────────────────┐ 0│00 @ /b /c /d ?? ДА \a \b \t \n \v { | } ^ │ задумано 1│~ ` к1 к2 к3 не ** \p ** \f \r \E [ \ ] ! │ 2│пр ' * # $ % & . , ; : + ( - ) / │ 3│_ " 0 1 2 3 4 5 6 7 8 9 < = > ? │ └────────────────────────────────────────────────┘ Явно ошибка в программе! Версию 1Б.609910 ред.196 от 09.02.25 срочно чинить! А псевдографика? ┌────────────────────────────────────────────────┐ вот это получилось 0 │ ─ │ ┌ ┬ ┐ ├ ┼ ┤ └ ┴ ┘ K L M ▀ ░ │ 1 │ ■ Q ╒ ╤ ╕ ╞ ╪ ╡ ╘ ╧ ╛ [ \ ] ▄ ▒ │ 2 │ ` a ╓ ╥ ╖ ╟ ╫ ╢ ╙ ╨ ╜ k l m ▌ ▓ │ 3 │ ═ ║ ╔ ╦ ╗ ╠ ╬ ╣ ╚ ╩ ╝ { | } ▐ █ │ └────────────────────────────────────────────────┘ ┌────────────────────────────────────────────────┐ а вот так было задумано 0│ ─ │ ┌ ┬ ┐ ├ ┼ ┤ └ ┴ ┘ & <- * ▀ ░ │ 1│ ■ * ╒ ╤ ╕ ╞ ╪ ╡ ╘ ╧ ╛ & * ▄ ▒ │ 2│ ∙ * ╓ ╥ ╖ ╟ ╫ ╢ ╙ ╨ ╜ & -> * ▌ ▓ │ 3│ ═ ║ ╔ ╦ ╗ ╠ ╬ ╣ ╚ ╩ ╝ & * ▐ █ │ └────────────────────────────────────────────────┘ Ну почти такая (если учесть что лишние символы просто не преобразуются...) Кстати, а как-то узнать об этом можно? Или тоже недоработано?... * * * В общем подведем некоторые итоги. Часть вторая ознаменовалась тем, что наконец то удалось реализовать некоторые средства для работы с текстовыми строками. Свои собственные, оригинальные, не прибегая к полиморфизму, сосредоточенные преимущественно в подсистеме ввода/вывода, поскольку сами тексты лежат во внешнем мире в виде файлов. Текстовых... Для этого к Фокалу пришлось добавить три (!) новых, отдельных, хотя и не совсем самостоятельных языка: язык форматных выражений, регулярных выражений (они же шаблоны, включая и генерирующий) и выражений стэковой арифметики. Правда пока только для целых чисел. (Плавающая арифметика неограниченной точности - напрашивается, но так пока и не реализована.) Явление, впрочем, не уникальное: подобным же под-языком можно считать и, например, грамматику арифметических выражений. В Фокале она простенькая, а вот например в языке Си включает и некоторые средства управления порядком действий... (А в так называемых "функциональных" языках ничего другого в сущности и нет.) Шарахаемся от полиморфизма, оставив его для третьего Фокала (если и когда он будет) - уже из принципиальных (можно сказать "спортивных") соображений: у нас же типа произведение искусства! (А вовсе не...) Языков с полиморфизмом пруд пруди, начиная со всё того же Бейсика. И все они... ну не то что бы совсем на одно лицо, но таки разные реализации одного и того же принципа с немножко разной грамматикой. Мы же решаем вопрос: а есть ли другой принцип, позволяющий сделать то же самое, но по-другому? А вот оказывается есть!... Нереализованными остались некоторые из кодировок, например та же ДКОИ или CP-855 - тоже ДОС`овская, но для братьев-славян - с дополнительными буковками, несколько потеснившими псевдографику. (И организованная совершенно хаотично!) Вообще-то их предполагается подгружать по-мере надобности так и не реализованным оператором Load (в виде некоторого описания) и выделять под них новые буковки - для перекодировок исключительно русские, зарезервировав латинские под будущие встроенные шаблоны других типов. Нереализованными остались постфиксы в языке шаблонов, которые бы позволили некий элемент абстракции. Например сопоставился фрагмент анализирующего шаблона с некоторой конструкцией, например целым числом, именем переменной или даже с целым арифметическим выражением, но в протокол для генерирующего шаблона запишет не подробное описание его структуры, как это он сейчас делает, а некий абстрактный факт, что это "число", "имя", "выражение"... Да, кстати: проблема генерирующих шаблонов решилась ведением "протокола". (Который - только один, то есть тоже глобальный, но если что, может быть получен (вставлен в А2) в виде текста спецоперацией форматного выражения ** и записан (удалён из А2) с помощью // которые сделаны для отладки, но уж ладно, пусть будут - мало ли для чего вдруг понадобятся.) Каждый элемент анализирующего шаблона, сопоставляясь со своим фрагментом строки, фиксирует в протоколе - с чем именно сопоставился. (Так как сопоставление идёт методом перебора с возвратами, то при возврате (то есть неудачном сопоставлении) протокол тоже "отматывается" назад до места удачного, и дальше пишется поверх...) Так например . (точка) сопоставившись с символом Х пишет в протокол: ".Х" а встроенный шаблон %ш напишет "шХ"; конструкция <...> так и пишет в протокол сначала открывающую скобку "<" а когда подойдёт к концу (то есть удачно отработают все заключенные в ней элементы) - закрывающую ">". (Напоминаю: весь шаблон целиком - это тоже вот такая конструкция.) Префиксы повторения "выгораживают" там себе кусочек места, сначала написав "*", а в конце - "|". Выбирающая конструкция [...] пишет в протокол "[" и после неё число - порядковый номер сработавшего элемента, после чего (через запятую) то, что он сам туда напишет. (Ну и закрывающую "]" - чисто для красоты.) Встроенный шаблон, распознающий символ уникода, пишет его код в виде числа, но тоже с префиксом "U". Есть так же средство записать нечто сложное и громоздкое, с чем сопоставился например встроенный шаблон %S выделяющий строку в кавычках. А вот по поводу постфиксов, типа :А где то что сгенерирует это А должно заменить в протоколе то, что написал туда элемент шаблона этому постфиксу предшествующий, не реализовано и даже не продумано - пока только общая идея. Каждый элемент генерирующего шаблона получает очередной элемент протокола и пытается, если простой, как-то использовать, или, если сложный - распределить между входящими в него элементами. Так в частности всё та же . (точка) вставляет абсолютно всё что получила один в один. А элемент, изображающий сам себя - просто игнорирует. В части второй не решена проблема с передачей в подпрограмму аргумента в виде текстовой строки: если функциям получать такие аргументы можно, почему подпрограммам - нет? Потому, что тогда под него потребуется локальная переменная, способная хранить строку. А от полиморфизма мы шарахаемся... Ладно, предположим это будет регистр форматного механизма с номером, соответствующим позиции аргумента. А старое его значение - временно где-то сохраняется и при возврате из подпрограммы будет восстановлено. Но при этом с одной стороны встаёт вопрос определения типа полученного аргумента (включая и его пропуск) для каждой позиции. А с другой - в сохранении и последующем восстановлении нуждается так же и содержимое самого аккумулятора. Хотя не факт. Но предпринимаются же меры по сохранению и последующему восстановлению А1 на время выполнения реакции на внешнее событие... Или это "другое"? ((Определение типа полученного аргумента, как частный случай факта наличия переменной, можно поручить функции FSUBr("имя_переменной", индексы...) передав ей в качесте имени переменной пустую строку. Пусть возвращает -1 если переменной с таким именем и индексами нет, 0 если есть, а +1 только в случае локальной переменной, которой соответствует аргумент в виде текстовой строки (а в саму переменную попала её длина на момент передачи параметров). Позволяют же нынче операторы Write и Eraze, вот тоже со строчным аргументом, выдать и истребить значения не всех переменных, а только этим аргументом указанных. А что бы и здесь? Но в дополнение ко всему этому, уж коли мы взялись манипулировать с переменными, надо бы еще цикл по всем наборам индексов некоторого массива. Например так: For имя_переменной; тело цикла... - без всякого знака = (равно) - индексы попадут в локальные переменные &0 &1 &2 (ну мы же не знаем один там был индекс или два, значит пусть будет и так и так). Впрочем, баловство всё это...)) Так же не решена мелкая проблемка с функцией FTMP которая пишет свой "побочный эффект" (при преобразовании времени в удобочитаемый вид) непосредственно в канал вывода, а не в А2. Хотя вроде бы можно сделать это по аналогии с FTEll со строчным аргументом - именем файла для его поиска в каталоге. Там "побочным эффектом" (который - имя найденного файла) управляет сама эта функция (с помощью следующего аргумента). Вроде бы можно и здесь следующим образом: не FTMP(N) а FTMP(N,). И кстати для FMIn и FMAx на случай строчных аргументов сделать примерно то же самое. Правда, непонятно как сравнивать строчки с числами. Да и между собой: только по длине, или таки лексикографически?... Хотя вот это - определённо нет: слишком много вариантов - пусть %r и %R этим занимаются. А результат возвращают через аккумулятор А1, благо он там в свободном доступе... * * * А вот далее уже будет ЧАСТЬ ТРЕТЬЯ - чистой воды фантастика и выдумки. * * * ...А исчо есть у нас такой аккумулятор А3 - чего это он, спрашивается, бьёт баклуши, можно сказать почти без дела болтается? Пусть пользу приносит! (Ну чем мы хуже кота Матроскина?!) В туалете на военной кафедре разговаривают два студента: - знаешь, чем майор Пронин отличается от обезьяны? И тут заходит майор Пронин: - ну и чем же я отличаюсь от обезьяны? - Ни-Чем, товарищ майор! - то-то у меня! Ну так чем же мы хуже кота Матроскина - тем, что нас не показывают по телевизору? Первое апреля, однако!... Однако, пошутили и хватит. А и в самом деле: для чего такого полезного, из того, что нам не хватает, можно пристроить аккумулятор, куда попадают объекты ввода/вывода? И чего, кстати, нам не хватает? Графики? Да! Ну графика, это вопрос отдельный, завязанный на образование "коллекций" из графических примитивов, каждая из которых исходно - один график, а в перспективе - изображение. Так, чтобы она могла выступать деталью других изображений. То есть чтобы на неё можно было сослаться как на единое целое, вызвать как подпрограмму. С параметрами, естественно... С параметрами? Ну предположим... Но у нас же уже есть подпрограммы - самые обыкновенные. Можно же написать такую, которая рисует нечто графическое... Но это - писать надо. А коллекция - нечто другое, что само по себе в процессе (чего ни будь) собирается. И параметры для неё это типа переместить, повернуть и отмасштабировать... Перекрасить в другой цвет? Да. Коллекция же типа на цвет завязана: это же исходно один из графиков, коих несколько штук мы строим в общих осях, а значит - разными цветами. И собирается она вот как раз по мере рассчета точек этого графика. А хранить её предполагается в виде одной из групп программных строк. Но тут вылезает необходимость автонумерации. (А может быть и псевдонимов из Фокала-2?) В отличии от подпрограммы, где строк, а вернее операторов - умеренно, в графике какой ни будь функции этих самых отдельных точек могут быть многие тысячи. И считать каждую из них оператором (типа: Viz Точка X,Y; который её и нарисует), то на одних буквах V раззоришься! Да и метки там нужны сильно не каждому. А только например когда хотим замкнуть "полигон"... Или таки лучше делать это в терминах "вершины - фаски" как в 3-Д моделях? Подумаем... Вот вершине - аналогу точки, каждой нужна собственная метка, чтобы фаски - аналоги полигонов на них могли ссылаться. Но их там тоже могут быть многие тысячи. И хотя у нас сейчас номер строки в группе уже не две цифры, а до четырёх, всё равно, боюсь, номеров не напасешься... Да и с построением графика по точкам оно как-то не очень... В общем тут еще думать и думать надо. (В процессе чего надо бы с языком ПостСкрипт и с Х-терминалом познакомиться...) Так что - нет, не графика... Тогда может быть устройства? Вот например ком-порт... Ком-порт то у нас якобы есть (под псевдонимом Ц) - ДОС`ом по умолчанию предоставляемый... Но во-первых в машине их у меня два. Во-вторых: а как на счет скорости передачи, да и прочих параметров? А в-третьих не только отдельные байтики, но и пакеты оттедова хочется. Например вполне конкретные, имени одного моего знакомого... Или есть изернетовский адаптер - тоже со своими пакетами. Или вот USB... Или канал азбуки морзе... Пока что она только встроенным динамиком пикает. (И то засада со скоростями: надо переходить на другой таймер - который 1000 Гц, а не системный - 18 с копейками. Его конешно тоже можно перепрограммировать, но что будет с часами?!...) Но ведь можно и вывести на любой свободный вывод того же ком-порта, или принтерного. И, кстати, ввести... Но там еще надо решить проблему QLF так до сих пор и не решенную. (Что мол оператор работает на ключе левой пяткою!...) Или подсмотреть у кого... Но вывод то можно сделать уже сейчас... Теплее, теплее... Но всё же еще не то. А вот есть у нас еще такая проблемка - "навигация"... Сейчас с точки зрения Фокала внешний мир это коллекция текстовых файлов. Разложенных, правда, по каталогам... Но с каталогами Фокал работает, обращаясь к ОС с помощью оператора ! (восклицательный знак). И, например, перебраться в другой каталог под ДОС`ом с помощью команды CD у него вполне получается. (Потому что процесс - один общий. Под UNIX`ом, полагаю, не получится.) А ищет он файлы с помощью отдельного средства (FTEll("имя_файла")) никак с остальной подсистемой ввода/вывода (почти) не связанного... Правда файлы встречаются еще и "бинарные", с которыми Фокал испокон веку работает с помощью спецфункции FCHR, таская ею байты поштучно. (А мы в части второй еще кое-что к этому добавили.) Но чтобы сделать с бинарным файлом что-то разумное, надо точно знать как именно он устроен - его формат. А форматов этих нынче развелось - как собак нерезанных... Но вот, полагаю, настало время (и/или появилась возможность) резко облегчить работу с некоторыми из них. На примете: .dbf .wav .html вернее .xml А еще и ком-порт, из коего приходят не отдельные байтики, а пакеты имени одного моего знакомого... (Лично им в моём присутствии изобретенные. И с тех пор используемые как минимум в трёх изделиях...) Этот самый пакет - последовательность байтов, состоящая из "преамбулы", заголовка, тела пакета, содержащего полезную информацию и контрольной суммы. Преамбула (FF FF 00) должна показывать что информационный мусор, сыплющийся из линии связи, закончился и начался вот этот вот пакет. (Коллега малость перестраховался...) Заголовок - 4 байта: общая длина пакета, код его типа, адреса отправителя и получателя. Контрольная сумма - один байт: просто сумма всех остальных байтов пакета (не включая преамбулу). Содержимое - в зависимости от типа. В смысле о чем договорились, как правило сами с собой. Подобное изобретают все, кому не лень; этот конкретный - исключительно ради конкретики. Ага. Этот самый файл типа .dbf известный как "реляционная база данных", хотя и не совсем типичный их представитель. Но давно и широко используется, можно сказать, стал уже стандартом де-факто... От типичной отличается тем, что самодостаточный, "самоопределяемый" - содержит внутри себя описание своих собственных полей - вложенный мини-файл... Впрочем, по порядку. Эта самая реляционная база данных - всегда коллекция файлов. Разных, но организованных на одном и том же принципе. Каждый файл - последовательность не отдельных байтиков, а "записей" одинаковой длины. Каждая из которых состоит из одних и тех же "полей". Где каждое поле содержит одно элементарное (для данной базы данных) значение. А наш механизм табулостопов это случайно не средство разделить текстовую строку вот на такие поля? Да. Но есть и существенные отличия: - во-первых поля - именованные - во-вторых границы полей - "непроницаемы" - и главное: поля - типизированные. (И этих типов - три вида...) А у нас все поля пока что безымянные и все одного типа: "фрагмент строки". Разумеется, в разных файлах и длина записи и набор составляющих её полей могут быть очень даже разными. Поэтому в оной коллекции есть как минимум один специальный файл, содержащий "метаинформацию" обо всех остальных: список их полей с их типом и размерами. Описание каждого поля - одна запись, собственные поля которой - фиксированные. А у файла типа .dbf перед последовательностью записей, есть еще и заголовок, где среди прочего и содержится его метаинформация. Вопрос: является ли вышеописанный пакет частным случаем записи, бо он тоже состоит из полей? (Если например откинуть преамбулу и контрольную сумму.) Подумаем... Этот самый файл типа .wav якобы содержащий вопли чьей то жены (ибо по аглицки произносится совершенно так же), заставшей мужа... (опустим подробности) оказывается, представитель целого семейства одинаково организованных файлов, содержащих в себе потоки. Вон в файловой системе NTFS эти самые потоки реализованы на уровне ФС, но в других-то ФС ничего подобного нет, вот и приходится... Фрагмент потока (жаргонное название которого как-то связано со свиньёй/чушкой из юго-восточной Азии) представляет собою запись переменной длины, или, если хотите, очень простой пакет, заголовок которого (8 байт) содержит только идентификатор (4 байта) указывающий его тип, и длину тела. Но если она нечетная, то тело дополняется нулевым байтом. Собственно весь файл - это один вот такой пакет. Но два типа потоков "RIFF" и "LIST" содержат в себе вложенные потоки, организованные в точности так же. (Но первые 4 байта - тоже идентификатор, указывающий что всё это такое.) Так что в один файл можно запихать не только два канала с разными именами (чтобы был стереоэффект) но и текстовое сообщение, что именно взбешенная супруга хотела всем этим сказать. И еще массу полезной (или вредной) информации... (Вообще-то стерео и даже квадроэффект достигается как-то по-другому: не два отдельных канала, а в один - пары или даже чеверки отсчетов... Вроде бы.) Вопрос: а еще у нас потоки есть? А как-же! Вот например содержимое самого обыкновенного файла вне зависимости от его типа. Да и тело пакета пожалуй что можно считать потоком. Вложенным или "встроенным". Хотя конешно в самую первую очередь поток это то, что на нас валится из линии связи, включая отдельные байтики и затесавшиеся меж ними пакеты, вот как вышеописанный. А из файла, который лежит себе спокойно и никого не трогает, поток организуется с помощью движущегося по нему указателя чтения/записи! Но его то хотя бы переставить можно... Этот самый файл типа .html или лучше всётаки .xml это тоже если хотите поток потоков... Или пакетов... Но вообще-то он - типа текстовый. Текстовый файл можно считать потоком не байтов а строк. Тоже, как и пакет, переменной длины. В разных ФС это организуется по-разному. Например в начале каждой строки - её длина... Но чаще всего конец строки (и начало следущей) указывается символом-разделителем строк. Который внутри строки не встречается. (Вернее как встретился - значит это и есть конец строки.) В кодировках на основе ASCII это команда ПС (перевод строки, он же '\n') или пара ВК ПС. И еще текстовый файл по-возможности не содержит "слепых" (непечатных) символов - не имеющих изображений. (Раньше не имевших - сейчас то картинки сопоставили всем.) Но вот сейчас в винде вишь взяли моду делать строку размером с целый абзац - что де программное обеспечение само разобьёт это на строки - по размеру окна, который может запросто измениться в любой момент по прихоти пользователя... Запустившие эту моду мелкомягкие недоумки вишь не знали, что для этих целей на нулевой странице ASCII среди управляющих символов есть специально для этого предназначенный символ-разделитель групп записей, ну или абзацев текста. Или и знать не хотели, а хотели воспользоваться тем, что уже есть - создали всем остальным проблему на ровном месте... Файлы типа .html и .xml впринципе тоже текстовые, но только в том смысле, что состоят из печатных символов. (А к разбиению на строчки там относятся примерно так же, если не хуже.) Разбиение на что-то (вот на такие же "пакеты") там с помощью своего рода скобок - "тегов". (Тег - "ярлык".) Именованных. Открывающая и закрывающая скобки выглядит как <имя> и имя> и между ними - тело пакета, где может быть как текст, так среди него и такие-же вот скобочки. Скобочки впринципе должны быть сбалансированы, но в .html с этим - бардак. Потому что по исходному замыслу это команды форматирования текста. При чем в виде скобок - только некоторые из них: типа выводить вот таким шрифтом не всё нижеследующее (пока на другой шрифт не переключат), а только заключенное в оные скобочки. За сим в .xml сделали: <имя/> - пакет без тела; скобка разом открывающая и закрывающая. И придумали .xhtml - где бардака поменьше. Внутри открывающей скобки (заголовка пакета) могут быть дополнительные поля - тоже именованные. Вида: имя="значение". У .xml все эти имена - какие сами выдумаем. И смысл у них такой, о котором сами с собою договоримся. А вот у других файлов этого семейства - типа уже договорились. В частности у .fb2 это элементы оформления книги: автор, название, разные виды заголовков, деление на главы и абзацы... У .html это команды управления просмотрщиком. Который сперва был совсем простеньким - умел выводить текст разного вида, размера и цвета (например курсив: текст (i потому что "итальянский")), но главное - умел переходить по ссылке: текст_изображающий_ссылку (если конечно ткнуть в эту ссылку мышкою) в результате чего и получается "гипертекст": несколько связанных вот такими ссылками файлов. Лежащих как на одной машине, так и на разных. И тогда этот не просто имя файла, а сетевой адрес. (Который тоже "путь", но сначала по сети, а потом уже по тамошней файловой системе.) А вот форматирование текста как изначально было дурацкое, так и осталось: штатных средств сделать по-человечески - чтобы абзац начинался с красной строки, а не отделялся пустой строкой от предыдущего, как небыло предусмотрено, так нет и не предвидится. И приходится по-всякому выёживаться... Ну и весь такой файл это один пакет: .... внутри которого еще два - заголовок и тело. Оформленные вот таким же образом. В начале правда еще иногда встречаются рудименты другого языка (известного как SGML, вот это: <?....?> - его скобочки, и коментарий вида: тоже его наследство) куда более замудрённого и громоздкого, нигде и никогда так всерьёз и не применявшегося. Однако с упорством, достойным лучшего применения, это таки суют якобы для обратной совместимости... Вопрос: а еще что-то типа вот таких ссылок у нас есть? А как же! Вон каталоги, вернее файлы в них. Правда штука это - закрытая. Операционная система работает с ними только сама - по заявкам "заинтересованных лиц"... Доступ только по именам, а позиционирование - фиг! (Хотя это где как.) Но тем не менее... В общем не будем тянуть кота за хвост. Была вот такая идея: ввести еще один канал ввода/вывода (типа для поиска файлов) направленный по умолчанию на устройство "Д", изображающее собою текущий каталог. И ежели открыть что-то под этим псевдонимом, то это и будет эквивалент команды !CD, каковая в ДОС`е работает (потому что процесс - один общий для всех) а в UNIX`е не должна. (Потому что для каждой выполняющейся программы процесс - свой. И есть места, куда даже король ходит пешком.) Но открыть можно будет только файл с признаком "d". (В точности так же, как на запись можно открыть только файл с признаком "w", то есть разрешенный для записи. А переключить канал вывода, тоже получится только на что-то открытое для записи...) Так что к R и W в псевдониме открытого файла надо добавить еще буковку D. Пользоваться этим каналом будут оператор Oper и функция FTEll("имя_файла") Вернее - уже. В смысле, оказывается так оно и было всегда, изначально. Только мы вот как-то внимания на это не обращали. А вот устройство Д... Да, таки надо сделать - а то и в самом деле: как же с "cd" под UNIX`ом? (Но гром не грянет, мужик не перекрестится! Пока вот как то и так обходились...) ((И, кстати, а вот открываем мы файл под псевдонимом Х, а на него как раз УЖЕ направлен один из каналов, ну например вывода... И что-то я не припомню, чтобы проверялось что файл открывается именно для вывода и что он вообще разрешен на запись - чтобы иначе была ошибка...)) Вспомним, что в том же UNIX`е у каждого файла аттрибуты: -rwx (что их три комплекта - пока несущественно) где вместо прочерка может быть a b c d... при чем "а" (самый обычный файл) никогда не пишут, так и оставляют прочерк. Вот "d" как раз и означает, что это каталог... А вот многие аналоги Нортон-Командера позволяют "заходить" например в архивы типа .rar .zip ... как в каталоги... Нечто типа каталога имеют в себе и файлы других типов (библиотеки подпрограмм например). Тот же самый файл .wav содержащий внутри себя именованные потоки, тоже бы подошел в этом качестве... И вообще любой файл, содержащий в себе записи, имеющие два обязательных "выделенных" поля: "имя" - текстовое и "поток" - либо встроенный поток, как у пакета, так и указатель на что-то подобное. Но это чисто на будущее...)) ...Но таки надо отличать средства "разукрашивания" текста (ну ладно, пусть "форматирования"), как тот же .html, где выкинь все эти урашательства и останется чистый текст, который и есть главное содержимое... Отличать от средств формирования структуры (как тот же .xml) - самоопределяющейся. Где текст это всего лишь значения её полей. А главное - сама структура. Хотя грамматика у них - вроде как практически одинаковая. И даже не "вроде как" и не "практически" а один в один! Имена и смысл тегов разные... Для форматирования текста есть еще масса других средств, начиная с UNIX`овского roff (а так же производных от него nroff, troff...), изначально сделанного чтобы управлять некоторой фотонаборной машиной. Про который я смутно помню только что к уже имеющемуся форматированию он относился бережно, и что каждая команда там занимала отдельную строку и начиналась с точки в первой позиции. (Такая вот UNIX`овская заморочка, что мол ничего осмысленного начинаться с точки не может. Там у них и в главном текстовом редакторе ed (стареньком, маленьком и неудобном, за то работающем всегда - в том числе в самых-самых тяжелых условиях, потому что с интерфейсом командной строки!) поставить одинокую точку в начале строки - это выйти из режима набора текста в командный.) Это и дурацкий и-би-эм`овский формат .rtf - крайне примитивный (как и сам аглицкий язык, что всегда вызывает непомерную сложность на следующем уровне), отличающийся тем, что скобки там уже есть - фигурные. Типа для сохранения контекста: всё что установлено в скобках, при выходе из них - отменяется. И восстанавливается то, что было до. И еще там есть команды: слова, начинающиеся с \ (обратной косой черты), и состоящие только из букв, потому что дальше впритык идёт числовой аргумент (в том числе, говорят, что и отрицательный, но я не видел), хотя может и отсутствовать. Еще эта самая обратная косая черта служит там экранирующим символом (для себя и для скобок). А вот всё остальное, что не скобки и не команды - сам текст. Это и макрогенератор ТеХ (где букву Е традиционно пишут смещенной вниз на пол строчки) и производные от него (типа LaTeX), который изначально придумал лично Дональд Кнутт, когда захотел пристойно изображать в своих статьях математические формулы. И там команды - тоже с символа \ Это и уже упоминавшийся ПостСкрипт - вполне себе полноценный алгоритмический язык, вернее его интерпретатор, каковой нынче встроен во все лазёрные и струйные принтеры. (Мелкопроцессоры, ими управляющие, вишь стали нынче весьма мощные.) А формат .pdf - суть программа на этом языке (немножко урезанном). Раньше-то всё было просто: разом и терминалом и принтером был телетайп (разновидность электрической пишущей машинки), украденный на почте - там еще на моей памяти с их помощью телеграммы передавали по четыре копейки за слово. (А у одного мужика в командировке деньги кончились, так он послал жене телеграмму из одного слова: "сторублируй"!) В электрической пишущей машинке электрического только электромотор, вращающий резиновый валик, около которого для каждого рычага-литероносителя пристроилась такая полукруглая деталька... Которую, лёгким нажатием кнопки на клавиатуре, слегка прижимает к этому валику. Дальше он её проворачивает, а она тащит за собою этот рычаг. Который и ударяя по бумаге через красящую ленту, оставляет на ней отпечаток литеры. (А то, боюсь, многие живую пишущую машинку уже и не видели...) В механической пишущей машинке надо было колотить по кнопкам со всей дури. При чем по разным - еще и с разной силою! Потому что это усилие распределяется по площади литеры. И если А или Т напечатаются нормально, Ж или Ш - бледно, то точка или запятая пробьют в бумаге дырочки! (Ну бумага-то всё стерпит, но перед ней - красящая лента...) Телетайп - это такая электрическая пишущая машинка, где на печатающий механизм воздействуют не клавиши напрямую, а электромагнитики. А клавиатура выдаёт им для этого коды нажатых кнопок. Которые можно пробить на перфоленту и потом быстренько передать по линии связи (так обычно на почте и делали) или посылать в линию связи сразу - чтобы их и удалённый телетайп отрабатывал... А он либо печатает буковку, либо что-то делает - что умеет. Немногое: ПС, ВК, ВШ, ГТ ЗВ (перевод строки - прокрутка на одну строку бумагоопорного валика, возврат каретки, возврат на шаг, горизонтальная табуляция, звонок - реально звякающий специательным колокольчиком)... Еще в нулевой странице ASCII (помним еще, откуда она?) была пара команд то ли для установки и сброса табулостопа в текущей позиции, то ли для переключения цвета (красящая лента нужна была двухцветная!), которые потом приспособили для переключения регистров... А всё остальное там - для оформления пакетов и управления коммутацией в телетайпетной сети. Далее, когда появились отдельно дисплеи (где маркер можно поставить в любую точку экрана), и отдельно - принтеры, в том числе игольчатые, где можно было изобразить что-то графическое, напрямую управляя отдельными иголками, вспомнили про "эскейп-последовательности". На нулевой странице ASCII есть две команды: АР1 и АР2 (она же ЕСЦ) - "авторегистр" 1 и 2 с кодами 10 и 27, первая из которых - "экранирующая": отбирает "командный" смысл у следующего после него кода, а вторая - наоборот: придаёт следующему после него коду специальный смысл, позволяя учинять дополнительные команды. Сначала такие команды были просты и немногочисленны. (Например как у VT-52 и его аналогов: в дополнение к вышеперечисленным ПС, ВК, ВШ, ГТ, ЗВ, еще несколько штук: после EСЦ одна буковка: A, B, C, D - перемещение маркера вверх, вниз, вправо, влево на одну позицию; H, I, J, K - маркер в начало экрана; обратный перевод строки (как и ПС, только на предыдущую строку, и если она крайняя - тоже с прокруткой изображения); стереть от маркера до конца строки и экрана. Сложная - одна: прямая адресация маркера: ЕСЦ Y и еще два байта - координаты, куда его поставить. И последняя: ЕСЦ Z, на которую дисплей, если он есть, должен был отозваться тремя байтами: ЕСЦ / Z.) Потом команды стали многочисленными, сложными и навороченными. (Как у VT-100 и его аналогов.) У принтеров - тоже. При чем кто-то придерживался неких стандартов де-факто, заимствуя систему команд, например, у принтера фирмы Эпсон; кто-то придумывал свои собственные... Но для более серьёзного полиграфического оборудования... А лазерный или струйный принтер тоже уже можно причислить к этой категории... В общем, в конце концов догадались, что чем передавать изображение по точкам, проще и быстрее построить его на месте по куда более компактному описанию... Уже упоминавшийся ПостСкрипт не спешит, как некоторые, рисовать указанные ему графические примитивы... Сперва он строит абстрактный "путь" - траекторию, добавляя эти самые примитивы к её концу. Возможно, замкнутую: там есть специальная команда для её замыкания - соединения конца с началом. (Возможно, это будут контуры буковки.) Которую, вместе с "листом", на коем она рисуется, можно как хошь перенести, повернуть или отмасштабировать. И вот только потом либо этот контур заливается указанным цветом, либо прорисовывается указанными линиями (некоторой толщины, а то и пунктирности)... Сам этот ПостСкрипт - вполне себе универсальный интерпретируемый язык общего назначения. (Который, если что, как и Фокал, можно например использовать в роли калькулятора.) Построен он на тех же принципах, что и язык Форт: "стэкового" типа. (У нас так сделаны %r и %R в форматном выражении оператора Write.) Всё с чем работаем - берётся со стека и что получилось - туда же помещается. (Только стэк у них у обоих неограниченной глубины, не то что у нас.) И Форт можно считать безтиповый, а вот у ПостСкрипта - полиморфизЬм. Хотя принцип, да - одинаковый, грамматика немножко разная. И слова... Разделяемые, кстати, просто пробелами... У Форта слова - из совершенно любых символов, а у ПостСкрипта некоторые символы таки "служебные": это все виды скобок, включая угловые, символ % (процент, с которого, кстати, там начинается комментарий до конца строки) и еще некоторые. Например текстовая строка там заключается в круглые скобки (а вовсе не в кавычки), последовательность действий, которую потом можно будет выполнить - в фигурные, а массив - в квадратные. При чем строчка там - "литерал": сразу помещается на стэк. А вот квадратные скобки действуют хитрО: открывающая помещает на стэк спецобъект "метка", а закрывающая берёт всё что после этой метки скопилось, делает из этого массив и помещает его на место метки. Соответственно, меж этими скобками делай что хошь. А вот внутри круглых - все символы один в один. Ну разве что экранирующий... Как всегда \ (обратная косая черта).) А то, что Форт безтиповый, а типы данных таки объективно есть, то например для самых обычных арифметических операций (+ - * /) нужно несколько разных комплектов слов чтобы обозначать их для целых одинарной и двойной точности, а так же еще беззнаковых и с плавающей запятой... В общем случае - громоздко получается. (А что мы хотим? Эта самая безтиповость в том и заключается, что какую операцию применил, такой значит и был тип!) Но в ограниченных частных случаях - просто, компактно и очень-очень быстро. Для ПостСкрипта всё наоборот... В общем, выражений в обоих языках никаких нет: буде захотели вычислить что-то по формуле - преобразуем её в обратную польскую безскобочную запись... (Ха! Между прочим - обход дерева.) Типа вместо 1+2+3 пишем 1 2 + 3 + а если 1+(2+3) то соответственно 1 2 3 + + только вместо + в ПостСкрипте придётся писать add. Впрочем, главное и там и там это "словарь", где все вот эти слова и ищутся. Словарь - пополняемый! У нас в калькуляторах %r %R ничего подобного нет: набор команд - фиксированный. Но в потоке символов, который - команды, таки выделяются константы, сразу помещающиеся на стэк и собственно команды, которые что-то делают. Так и у них - "литералы" (например числа или строки) и просто слова, которые ищутся в словаре. (Но /слово - будет литералом.) Ищутся - в словарях: их там несколько - целая стопка (кипа, стэк!) - начиная с верхнего. Словарь, кстати, изображается: << ... >> где внутри - набор пар: ключ-значение. Да, можно изобразить и вот так, или создать пустой: N dict но сразу из N элементов. (И словарь и строка и массив отнюдь не резиновые - вот как создали, так и.) После чего хоть перекинуть его на стэк словарей командой begin и он станет "текущим" (а удалить его потом отуда командой end, но можно и получить на стэк копию текущего командой currentdict) и заполнять: /имя значение def (буде оное значение - литерал, получится переменная; буде нечто выполняемое, типа {...} - подпрограмма получится). А можно заполнять прямо здесь, обращаясь как с массивом или строкой - командами put и get. (Однако, имеем в виду: вот мы положили на стэк словарь, а так же то что будет ключом и значением; выполнили команду put которая туда их запишет. Но со стэка то она их все три удалит! И если мы этот свой словарь заранее не припрятали в переменную или хотя бы не продублировали, то он - тю-тю! Разве мы хотели этого?...) И вообще всё это сильно напоминает детскую загадку: "как положить слона в холодильник?". Да очень просто: открыть холодильник, положить слона, закрыть холодильник. А жирафа? В точности так же, только сначала слона надо вытащить... В языке Форт такие фокусы со слонами и холодильником как-то не. Там всё честно: вот до сих память занята, а дальше свободна. Занимаем еще сколько-то ячеек, положив туда то, что составит заголовок новой словарной статьи, включая её имя и ссылку на предыдущую. Засыплем содержимое, или даже просто зарезервируем место, передвинув границу. Переставим туда указатель начала словаря. Всё! (Всё это буквально парочка команд - всё давно автоматизировано! Но надо хорошо понимать, что делаешь.) Но словари уже сразу обширные, слов тама ентих таки дюже много. Сотни. А учитывая что всё это чужая лексика, чуждая, можно даже сказать что вражеская... (Пока нет? Ну это нам только так кажется!) Да, и Форт и ПостСкрипт позволяют вводить новые слова. Ну так переименовать всё к такой-то бабушке!... Но нам с нашим языком флективного типа всё равно не больно то удобно. Это скорее для тех, кто пишет и думает неизменными иероглифами. Вот китайцам, полагаю, самое оно... Впрочем, хотя "опора на естественный язык" это вот как раз для них, а для нас - глупость и профанация (да и тупое запоминание - как нож острый, вся надежда на правильные ассоциации), но у каждого свои проблемы, и если у нас они более синтаксические, то у них имеющая место быть контекстозависимость... Пакость еще та! Нет, если бы оная непомерная омонимия выявляла реально полезные ассоциации между обозначаемыми вещами... Вот как наши гнёзда однокоренных слов. Но если они таки больше случайные, то ведёт это к: от просто злоупотребления классификационными наборами и проистекающей из этого мистики, до острого дефицита носителей разума... Ну всё, автора понесло... Ты про ёлку-то не забыл, боярин дубовый?! (Ц) Ну это я так - для общего развития. Да не собираемся мы (по крайней мере пока) выковыривать тексты из .rtf или .pdf файлов! У нас сейчас другая проблема: перемещение по ссылкам - вот та самая "навигация". * * * Итак, что видим в вышеизложенном? Запись; поле записи; пакет, который тоже совокупность полей; поток - обобщение файла и линии связи, а так же тела пакета. И три вида содержимого эти самых полей: число, строка и вот поток. При чем в двух вариантах: как ссылка и "вложенный". Но это нам пока без разницы. Главное: вот как раз для трёх наших аккумуляторов! Теперь, согласно новой (вот только что изобретенной) моде, оператор Oper оперирует вовсе не какими-то там файлами, а потоками! И в аккумулятор А3 тоже попадает указатель на такой поток. Но если раньше он использовался только и исключительно для того, чтобы функция FTEll могла сообщить об этом файле (потоке) кое-какие сведения, то вот прямо сейчас надо срочно придумать, как бы расширить ареал его применения. По аналогии с тем, как мы в предыдущей части сделали для А2: с одной стороны пусть используется везде, где бы могло пригодиться вот такое значение, а с другой - чтобы туда попадали все значения такого типа. Ну и поищем для этого типа переменные... Ага-ага, ну и кому, спрашивается еще кроме всё той же FTEll это может понадобиться? Разве что оператору Oper, в распоряжении которого как раз и находится то, что с некоторыми натяжками можно считать "переменными": псевдонимы открытых файлов, коим он выделяет буковки во временное пользование... ((Кстати, а не наклёвываются ли у нас еще и следующие аккумуляторы? А то мы вроде как собрались тоже выделять во временное пользование буковки под типы файлов и под кодировки, подгружая всё это так пока и не реализованным оператором Load... Ну, слава Аллаху, вроде бы нет. Нам бы еще вот с А3 как ни будь разобраться... И вообще бог троицу любит, да и в операторе If у нас только три направления... Так что это был бы явный перебор!)) Итак, определились что кроме FTEll и Oper аккумулятор А3 вроде бы никому больше не нужен. (Ну разве что введём для чего ни будь еще одну функцию, для которой, так же как и для FTEll, это будет неявный параметр.) Результат работы FTEll("имя_файла") по поиску файлов в каталоге уже (изначально) попадает в А3. Теперь еще надо чтобы туда попадали значения полей ссылочного типа. И надо придумать, как использовать А3 в Oper. Пока что чисто синтаксически... Ну ясный пень, как... Где там прохлаждается главный баламут всея Фокала?! (А подать сюда Ляпкина-Тяпкина! (Ц)) А то он вроде бы уже во всех операторах ввода/вывода отметился? А ан в операторе Oper еще нет - упущение! Так что: Oper % Псевдоним; или даже: Oper %ХХХ Псевдоним В первом случае имеем аналог присваивания: поток из А3 теперь будет еще и под указанным в операторе псевдонимом. А "аналог" потому, что это ведь могла быть всего лишь ссылка на файл, а вот теперь он открывается. Так что если раньше, найдя конкретный файл с помощью FTEll("шаблон") мы должны были предпринять меры, чтобы его имя попало в А2 в качестве "побочного эффекта" и уж только потом... А сейчас можно вот так - напрямую. И как же это я раньше до этого не додумался! Но теперь (хотя я еще и не придумал - как?) в А3 сможет попасть значение некоторого хитрого поля, например типа "мемо" в .dbf, или вложенный поток некоторого пакета. И мы теперь сможем его открыть так, как будто это вполне обычный файл. (А то как еще до них добраться?) Вот нам и навигация!... Но сразу всплывает проблемка: таких потоков может быть много, очень много, а псевдонимов - мало. При открытии нового файла, тот, который уже был открыт под данным псевдонимом - закрывается... Ну и как его потом открыть по-новой? Особенно, если мы прошлый раз шли к его открытию неведомо какими партизанскими тропами... Или если, например, в некой подпрограмме понадобилось временно чем-то попользоваться - после себя надо бы восстановить всё как было. А если она при этом еще и рекурсивная... Имя файла надо знать! А мы его знаем? Ой, не факт... (Кстати, штатных средств узнать имя открытого под таким-то псевдонимом файла что-то не припоминается - как то никому не надо было... Ну не вывод же оператора Write Oper; ловить и анализировать!) А даже если бы и знали... (Положение указателя чтения/записи тоже восстановить не вредно...) Но сейчас-то дело существенно усложняется. И вообще полезно как-то запомнить путь, которым мы вот сюда пришли... В общем вводим в псевдоним еще одну буковку, например S, предписывающую предыдущий файл, открытый под этим псевдонимом, не закрывать, а "отодвинуть" куда-то вглубь. На предмет, когда новый будет закрыт, вернуть всё как было. А при переключении канала ввода или вывода эта самая S должна указывать на необходимость сохранения информации о том, куда он был направлен для последующего восстановления. Например если вдруг файл, на который его направили, будет закрыт. Или... (Надо придумать, как переключить не куда-то а обратно. Может быть что-то типа Op #R; если это к примеру канал ввода. Хотя символ # это вполне добропорядочная буква... А нужен такой, что псевдонима образовать не может... Может: Oper % и всё - без всякого псевдонима?) Во-втором случае это самое ХХХ может быть например буковкой М чтобы добраться до мета-информации данного потока. Или буковкой U чтобы что-то сделать с USB, или буковка К (а может быть таки Ц) - чтобы с компортом... (Ну например так: Open %К2=9600n2 П; - открыть под псевдонимом П ком-порт 2 и установить скорость 9600 бод при двух стоповых битах и без контроля четности.) * * * Теперь надо что-то делать с полями. Много чего. Но для конкретики предположим что у нас под псевдонимом П открыт уже существующий файл типа .dbf который, как известно, содержит в себе собственную метаинформацию. Ознакомиться с нею мы можем следующим образом: Oper () П; Oper %m QrS; Oper QrS; Write %m; Oper "" Q; Из чего должны сделать вывод, что у нас так же есть и встроенный формат %М оператора Write, преобразующий вот такие-вот метафайлы в пригодный для человеческого восприятия вид. Однако, мы-то ознакомились, а что про этот файл знает программа? Возможны варианты. Предположим, что она знает что там есть поле с именем "имя_поля", от которого не ждёт никакого подвоха. Тогда всё как обычно: Op П; Ask :имя_поля, Х И всё: если оно, как обычно, текстовое, то его содержимое попадёт во входной буфер оператора Ask после чего обычным образом будет вычислено находящееся там выражение... Из чего, кстати, следует, что при переходе к следующему ИМЕНОВАННОМУ полю, аккумулятор А2 так же автоматически очищается, как и при переключении канала ввода. А ежели поле числового типа, то тогда еще проще: этот самый числовой тип напрямую (минуя А2) преобразуется во внутреннее представление и попадает в переменную Х. Ага. А вот если ожидает? Подвох же здесь в том, что это поле и не числовое и не строчное. (Если не ожидала - будет ошибка.) А ожидает она следующим оригинальным образом: Op П; Ask :имя_поля = A, B, C, D....; Понятно? ...Пришлось, панимашь, таки задействовать форму с присваиванием... Что тут будет? А вот: содержимое поля попадает в один из трёх аккумуляторов. А в переменную A - сведения, в какой именно (его вид): 0 - число, +1 - строка, и -1 - вот то самое, что в А3. А в B, C, D... - следующие этого поля параметры... Уж (пока) не знаю какие. Ну может быть более конкретный тип, размер, смещение поля в записи... (Если просто Ask = ...; - то для текущего поля.) А если для оператора Type, то всё, надо понимать, в другую сторону? Ага. А конкретнее? В текущее поле копируется содержимое одного из этих трёх аккумуляторов. И, возможно, (то есть если это возможно) устанавливаются его параметры. В данном конкретном случае - если такого поля еще небыло, что должно бы вызвать ошибку при позиционировании, а .dbf-файл пока пустой, состоит только из заголовка, каковой и пополняется. Ага-ага: а ежели .dbf-файл как был так и остался пустой, куда-же, спрашивается, делось якобы записанное туда значение из аккумулятора? Вот тут и всплывает необходимость в буферизации вывода! Вернее дело обстоит так, что с базами данных, типа вот этой вот .dbf, полагается работать "транзакциями". Чтобы, если что, она не зависла в промежуточном состоянии... В данном конкретном случае мы должны читать его и писать не побайтно, а вот такими (какие у него есть) записями. То есть к каждому такому файлу привязан буфер под одну запись, и всё что мы пишем - мы пишем туда. А что читаем оператором Ask - оттуда. По поводу автоматического чтения следующей записи мы еще подумаем, а вот запись оператором Type находящейся в оном буфере записи только и исключительно когда в его списке вывода встретится ! (восклицательный знак) для строк указывающий, что мол надо учинить конец строки. Вот где-то так. Но это только для .dbf Файл, содеражщий потоки, на вроде .wav надо сперва открывать как каталог. И выбирать там один из потоков по имени. С потоком пакетов по-видимому придётся делать две разные вещи - либо объединять содержимое всех следующих друг за дружкой однотипных пакетов в один непрерывный поток. Автоматически. Открыв этот виртуальный поток под другим псевдонимом. Либо таки рассматривать каждый пакет индивидуально - как "дейтаграмму". (Иковерканное "датаграмма" - аналог телеграммы.) Так же как отдельную очередную запись из .dbf Самое непонятное - что делать с .html? С .xml вроде как следует поступать так же как с поледовательностью записей. Но обратим внимание - каждая такая запись содержит два выделенных поля: имя тега и содержимое - поток. То есть как полагается каталогу. Отличие только в том, что там все имена уникальные, а тут нет. А еще - там в заголовке могут быть какие угодно дополнительные поля. В том числе и заранее неизвестные. У .html - то же самое. Но там это объективно сущетсвующие параметры просмотрщика, которым временно (до закрывающей скобки) придаются новые значения. Получается, что у .html-евской записи ну просто куча нигде заранее не описанных каких угодно полей (правда все одного типа: фрагмент строки), а не как у .dbf - фиксированный набор, и целый стэк значений для каждого. То есть если даже такого поля никогда небыло, то обращение к нему это не ошибка, но значение у него - строка нулевой длины. Как это сделать - впринципе понятно. Но черт возьми, как же это громоздко получается! * * * Следующая наша проблема это описание полей. Именованных. Оно должно быть организовано по аналогии с устанавливающим табулостопы спецкомментарием из одних двоеточий. Что-то типа: :имя_поля1 :имя_поля2 :имя_поля3... ! Ага. Но с одной стороны надо еще указывать тип поля. (Например как в .dbf, где тип - это одна буковка.) При чем запросто может получиться так, что имя поля плюс тип в нём не помещаются. И тогда размер поля тоже придётся указывать явным образом. А вот с другой: если табулостопы привязаны к терминалу, и существуют в единственном экземпляре (т.е. глобальные), то тут всё не так: у каждого файла - свой набор полей. Пусть даже и частично одинаковых. За сим требуется привязка к файлу, что-то типа: @имя_файла :имя_поля1: имя_поля2... ! Вот только имя файла здесь не очень-то подходит. (Ну разве что в качестве значения по-умолчанию.) Фактически это метафайл. (Вернее метапоток - поток метаинформации для некоторого потока.) Он может быть встроенным в заголовок файла, как для .dbf или и вовсе виртуальным. И плюс к этому нам пожалуй пригодился бы виртуальный каталог - список вот таких метафайлов, автоматически пополняемый по мере открытия файлов или ввода вот таких вот описаний, изобретением которых мы вот сейчас и занимаемся... Как мы это собираемся использовать? Ну например так: Open псевдоним = имя Помнится, когда-то давно, это было полным синонимом для: Open "имя" псевдоним Впрочем это была "учебная" реализация базового Фокала "первый блин", который и в самом деле получился тогда немножко комом... Сейчас же будет назначение потоку новой структуры. С текущего места... Итак, если поток - это поток записей, то и мета-поток к нему это тоже поток записей, каждая из которых описывает одно поле. А мета-мета-поток - один для них для всех... Или не один а всего лишь одинаковый? Потому что хочется, чтобы к каждой такой записи, описывающей одно поле, прилагалось еще и его значение. Что значит "прилагалось"? Надо уточнить. Пока что я это понимаю так, что вот у меня есть открытый файл (поток); его указатель чтения/записи указывает на некоторый его элемент; этот самый элемент (запись разумеется) уже считан и находится в связанным с этим файлом буфере. А каждая запись метапотока имеет указатель на то поле этого буфера, которое описывает. На предмет получения оттуда содержащегося в этом поле значения. Ага-ага: теперь надо уточнять, что значит здесь слово "получение"? Ну как "что" - вот например у нас есть файл (поток) открытый под псевдонимом П; под псевдонимом Q открываем его метафайл: Ope () П; Ope %M Q и теперь написав что-то типа: Ope QS; Write %M; Ope %; получаем нечто типа: @имя_метафайла :имя_поля1 = значение1 :имя_поля2 = значение2 .... ! (или ; и продолжение в следующих строках - если не поместилось в одну). Где после знака = (равно) - содержимое соответствующего поля из текущей записи файла, открытого под псевдонимом П. Ага. Сделать то вот такое для одного этого частного случая вполне можно. А для общего? Для общего получается, что эти самые "значения" - значения одного и того-же поля. Уж не знаю (пока) как оно называется, но не слишком ли круто? Получается, что у каждой записи метафайла это поле будет разное - в точности того типа, как описывается самой этой записью. Ну и что, что метафайл - виртуальный. Договорились же, что в потоке записей, все записи одинаковые! Ну ладно, пусть таки это поле типа указатель на поток из одного элемента. При чем со свойством автоматического разыменования, как в Си-плюс-плюсе то ли "указатель" то ли "ссылка" (вечно я названия этих адресных типов путаю). Ха! А как он вообще выглядит, этот самый "общий случай"? Как... Ну например мы откроем вот этот вот мета-файл, найдя его в оном виртуальном каталоге мета-файлов. Пока правда не знаю как это сделать. В смысле как переключить на него канал "Д", но возможно придумаю. И тогда, спрашивается, на какие именно поля какого из нескольких сответствующих данному метафайлу потоков будут направлены все эти ссылки? Учитывая, что может их и вправду несколько, а может и ни одного - все уже закрыты или еще даже не открывались (и/или даже и не созданы), но было заранее введено описание... О котором я опять благополучно забыл! Ну в общем - да: получается что метафайл сам по себе, но будучи открыт, он всегда связан с каким-то конкретным файлом, или же пока ни с каким - тоже виртуальным - только чтобы было где хранить начальные значения, буде они указаны в описании. Форму и вид которого я так старательно отлыниваю придумать. Придумать осталось каким образом обозначаем параметры поля. Имя это имя, оно после двоеточия обязательно первое. Значение всегда последнее - после знака = (равно). А вот остальные параметры... - номер по порядку - например #число - смещение от начала записи - например +число - размер поля - например *число - тип поля - например /буковка (пусть такая же как в .dbf) - комментарий - после ; или ! и до конца строки Пусть там где нужно число, может быть числовое выражение в любых скобках, а там где буковка... Вот если бы нам надо было ограничиться только файлами типа .dbf то позаимствовали бы оттедова оные буковки и всё: "C" - символьное "N" - числовое "L" - логическое "D" - дата (типа: ГГГГММДД) "F" - число с плавающей запятой "M" - "мемо" - хранящийся где-то отдельно текстовый комментарий (вернее десять цифр - его адрес в файле типа .dbt) Вот только для общего случая этого категорически недостаточно. Потому что например число здесь вовсе не двоичное, а последовательность цифр. И вообще похоже, что этот самый .dbf (вернее его основной поток) типа насквозь текстовый. И вот с помощью этого не получится как-то описать даже заголовок самого этого . dbf-овского файла. И, кстати, в форматном преобразовании (это которое "еще более форматное", чем само форматное выражение оператора Write %): ={X%формат} и #{Y%формат} у нас вообще-то примерно такая же проблема. А еще хочется, чтобы буковки, обозначающие типы здесь и форматы там были одни и те же или хотя бы как-то между собою согласованы. А там у нас - буковки, позаимствованные у Сишных функций printf() и scanf() (с их же помощью и реализовано): d i o u x - целое число (десятичное, 8 и 16-ричное, беззнаковое...) b - тоже целое, но бинарное e f g - плавающее c s - символы При ближайшем рассмотрении ни то ни то нам не годится. Подходит только в первом приближении: у функций printf() и scanf() формат должен описывать в первую очередь их аргументы, что для нас неактуально - нам надо только то, во что их преобразовать. Так что буковки D O X B обозначающие десятичное, восьмеричное, шестнадцатиричное и двоичное (какового, кстати у printf() и scanf() нет) - да, пригодятся. А вот U указывающее что аргумент беззнаковый... А вот I... Ха, это только при выводе I дублирует D, а при вводе он распознаёт числа вида 0xNNN 0NNN 0bNNN


продолжение следует