(******************************************************************************)
(**)                        ОТДЕЛ Читать;                                   (**)
(******************************************************************************)
(* НАЗНАЧЕНИЕ: чтение из потока                                               *)
(*                                                                            *)
(* ПРИМЕЧАНИЯ:                                                                *)
(*   для предварительного чтения используется промежуточная память (ПП),      *)
(*   которая заполняется заранее                                              *)
(******************************************************************************)

ИСПОЛЬЗУЕТ 
  ОБХОД, 
        (*     \Глагол\                *)
  ОС      ИЗ "..\..\..\Отделы\Обмен\",
        (*     \Глагол\                *)
 оЗнака   ИЗ "..\..\..\Отделы\Иное\";

ПОСТ
                                       (* обработка знаков *)
  знУни-=0; (* заголовок 0FFH,0FEH *)
  знВин-=1; (* по 1251 таблице *)
  знДос-=2; (* по 866  таблице *)
                                       (* размер ПП *)
  Размер=4096; 

ВИД
  ДЦепь=ДОСТУП К ЦЕПЬ;

  ППамять=РЯД Размер ИЗ ОБХОД.Ячейка;

  Поток-=ДОСТУП К ОписаниеПотока;
  ОписаниеПотока=НАБОР
    память:ППамять;    (* ячейки ПП              *)
    занято:ЦЕЛ;        (* ячеек занятых данными  *)
    выбрано:ЦЕЛ;       (* уже выбранных ячеек    *)
    фссылка:ЦЕЛ;       (* ссылка на файл в ОС    *)
    фпозиция:ЦЕЛ;      (* позиция в файле        *)
    началоСтроки:КЛЮЧ; (* ВКЛ, в начале строки   *)
    видЗнаков+:ЦЕЛ;    (* обработка знаков       *)
  КОН;

(******************************************************************************)
ЗАДАЧА Наполнить(поток:Поток);
(* Наполняет ПП потока данными из файла. *)
УКАЗ
  поток.занято:=ОС.ЧитатьФ(поток.фссылка,поток.память,Размер);
  поток.выбрано:=0
КОН Наполнить;

(******************************************************************************)
ЗАДАЧА Открыть-(имяфайла-:ЦЕПЬ):Поток;
(* Открывает новый поток. *)
ПЕР
  поток:Поток;
  фссылка:ЦЕЛ;
УКАЗ
  фссылка:=ОС.ФДляЧтения(имяфайла);
  ЕСЛИ фссылка = 0 ТО ВОЗВРАТ ПУСТО КОН;
  СОЗДАТЬ(поток);
  поток.фссылка:=фссылка;
  поток.фпозиция:=0;
  поток.началоСтроки:=ВКЛ;
  поток.видЗнаков:=знВин; (* будет знУни!! *)
  Наполнить(поток);
  ВОЗВРАТ поток
КОН Открыть;

(******************************************************************************)
ЗАДАЧА Закрыть-(поток+:Поток);
(* Закрывает поток. *)
УКАЗ
  ОС.ЗакрытьФ(поток.фссылка);
  поток:=ПУСТО
КОН Закрыть;

(******************************************************************************)
ЗАДАЧА Конец-(поток:Поток):КЛЮЧ;
(* Возвращает ВКЛ, если в потоке уже окончились данные. *)
УКАЗ
  ВОЗВРАТ поток.занято = 0
КОН Конец;

(******************************************************************************)
ЗАДАЧА Позиция-(поток:Поток):ЦЕЛ;
(* Возвращает позицию, отсчитываемую от начала файла. *)
УКАЗ
  ВОЗВРАТ поток.фпозиция
КОН Позиция;

(******************************************************************************)
ЗАДАЧА СменаПозиции-(поток:Поток; поз:ЦЕЛ);
(* Меняет позицию чтения на <поз>, отсчитываемую от начала файла. *)
УКАЗ
  ОС.СменаПозицииФ(поток.фссылка,поз);
  Наполнить(поток);
  поток.фпозиция:=поз
КОН СменаПозиции;

(******************************************************************************)
ЗАДАЧА Ячейки-(поток:Поток; память+:ОБХОД.Ячейки; размер:ЦЕЛ);
(* Читает в <память> определенное число ячеек.
 * При невозможности чтения заполняет ячейки нулями. *)
ПЕР
  поз:ЦЕЛ;
УКАЗ
  ОТ поз:=0 ДО размер-1 ВЫП
    ЕСЛИ Конец(поток) ТО 
      память[поз]:=0
    ИНАЧЕ
      память[поз]:=поток.память[поток.выбрано];
      УВЕЛИЧИТЬ(поток.выбрано);
      УВЕЛИЧИТЬ(поток.фпозиция);
      ЕСЛИ поток.выбрано >= поток.занято ТО
        Наполнить(поток)
      КОН
    КОН
  КОН
КОН Ячейки;

(******************************************************************************)
ЗАДАЧА БудутЯчейки(поток:Поток; память+:ОБХОД.Ячейки; размер:ЦЕЛ);
ПЕР
  поз:ЦЕЛ;
УКАЗ
  ОТ поз:=0 ДО размер-1 ВЫП
    память[поз]:=поток.память[поток.выбрано+поз]
  КОН
КОН БудутЯчейки;

(******************************************************************************)
ЗАДАЧА БудетЗнак-(поток:Поток):ЗНАК;
ПЕР
  знак:ЗНАК;
  уз:ЯЧЦЕЛ;
УКАЗ
  ЕСЛИ Конец(поток) ТО ВОЗВРАТ 0X КОН;
  ЕСЛИ поток.видЗнаков = знУни ТО
    БудутЯчейки(поток,знак,2);
    ЕСЛИ (поток.фпозиция = 0) И (знак = "#FEFF") ТО
      Ячейки(поток,знак,2);
      БудутЯчейки(поток,знак,2)
    КОН
  ИНАЧЕ
    БудутЯчейки(поток,уз,1);
    ЕСЛИ поток.видЗнаков = знДос ТО
      знак:=оЗнака.ИзДос(уз)
    ИНАЧЕ
      знак:=оЗнака.ИзВин(уз)
    КОН
  КОН;
  ВОЗВРАТ знак
КОН БудетЗнак;

(******************************************************************************)
ЗАДАЧА ЗнакЦепи-(поток:Поток):ЗНАК;
(* Считывает знак из потока. *)
ПЕР
  знак:ЗНАК;
  уз:ЯЧЦЕЛ;
УКАЗ
  ЕСЛИ поток.видЗнаков = знУни ТО
    Ячейки(поток,знак,2);
    ЕСЛИ (поток.фпозиция = 2) И (знак = "#FEFF") ТО
      Ячейки(поток,знак,2)
    КОН
  ИНАЧЕ
    Ячейки(поток,уз,1);
    ЕСЛИ поток.видЗнаков = знДос ТО
      знак:=оЗнака.ИзДос(уз)
    ИНАЧЕ
      знак:=оЗнака.ИзВин(уз)
    КОН
  КОН;
  ВОЗВРАТ знак
КОН ЗнакЦепи;

(******************************************************************************)
ЗАДАЧА Строка-(поток:Поток; цепь+:ЦЕПЬ);
(* Считывает строку знаков из потока в цепочку знаков. При этом
 * конец строки "#000D#000A" не записывается в конец этой цепочки. *)
ПЕР
  занято:ЦЕЛ;
  знак:ЗНАК;
УКАЗ
  занято:=0;
  КОЛЬЦО
    ЕСЛИ (занято >= РАЗМЕР(цепь)) ИЛИ Конец(поток) ТО
      поток.началоСтроки:=ОТКЛ;
      ВЫХОД
    КОН;
    знак:=ЗнакЦепи(поток);
    ЕСЛИ  знак = 0DX ТО
      поток.началоСтроки:=ВКЛ;
      знак:=ЗнакЦепи(поток); (* 0AX *)
      ВЫХОД
    АЕСЛИ знак = 0AX ТО
      поток.началоСтроки:=ВКЛ;
      ВЫХОД
    КОН;
    цепь[занято]:=знак;
    УВЕЛИЧИТЬ(занято)
  КОН;
  ЕСЛИ занято < РАЗМЕР(цепь) ТО
    цепь[занято]:=0X
  КОН
КОН Строка;

(******************************************************************************)
ЗАДАЧА ПропуститьСтроку-(поток:Поток);
(* Пропускает в потоке строку знаков. *)
УКАЗ
  поток.началоСтроки:=ОТКЛ;
  ПОКА НЕ Конец(поток) ВЫП
    ЕСЛИ ЗнакЦепи(поток) = 0AX ТО
      поток.началоСтроки:=ВКЛ;
      ВОЗВРАТ
    КОН
  КОН
КОН ПропуститьСтроку;

(******************************************************************************)
ЗАДАЧА ДлинаЦепь-(
                         поток:Поток
                 ):ДЦепь;               (* :ДОСТУП К ЦЕПЬ; *)
(* Читает длину цепочки и саму цепочку во вновь распределённую память *)
ПЕР
  длина,поз:ЦЕЛ;
  дцепь:ДЦепь;
УКАЗ
  Ячейки(поток,длина,4);
  СОЗДАТЬ(дцепь,длина+1);
  ОТ поз:=0 ДО длина-1 ВЫП
    дцепь[поз]:=ЗнакЦепи(поток)
  КОН;
  дцепь[длина]:=0X;
  ВОЗВРАТ дцепь
КОН ДлинаЦепь;

(******************************************************************************)
ЗАДАЧА НачалоСтроки-(поток:Поток):КЛЮЧ;
(* Возвращает ВКЛ, если задача Строка или задача ПропуститьСтроку в прошлый раз
 * прочитали конец строки "#000D#000A". *)
УКАЗ
  ВОЗВРАТ поток.началоСтроки
КОН НачалоСтроки;

(******************************************************************************)
ЗАДАЧА Целое-(поток:Поток; цОтвет+:ШИРЦЕЛ; вОтвет+:ШИРВЕЩ):КЛЮЧ;
(* Считывает из потока знаковое представление числа.
 * Если это представление целого числа, то возвращает ВКЛ и записывает
 * значение числа в <цОтвет>, иначе возвращает ОТКЛ, а значение записывает
 * в <вОтвет>. 
 * Следующий за числом знак остаётся в потоке. *)
ПЕР
  i,m,n,d,эксп:ЦЕЛ;
  цифры:ЦЕПЬ[64];
  ошибка:КЛЮЧ;
  мант:ШИРВЕЩ;
  знакМант,знакЭксп:ЦЕЛ;
  знак:ЗНАК; (* будущий знак *)

(******************************************************************************)
  ЗАДАЧА ЗнакЦифра(знак:ЗНАК):КЛЮЧ;
  УКАЗ
    ЕСЛИ ("0" <= знак) И (знак <= "9") ТО
      ВОЗВРАТ ВКЛ
    ИНАЧЕ
      ВОЗВРАТ ОТКЛ
    КОН
  КОН ЗнакЦифра;
   
(******************************************************************************)
  ЗАДАЧА ЧитатьЗнак;
  (* Считывает будущий знак из потока. *)
  ПЕР
    прошлыйЗнак:ЗНАК;
  УКАЗ
    прошлыйЗнак:=ЗнакЦепи(поток);
    знак:=БудетЗнак(поток)
  КОН ЧитатьЗнак;

(******************************************************************************)
  ЗАДАЧА ДесятьВ(x:ЦЕЛ):ШИРВЕЩ;
  (* 10 в степени <x> *)
  ПЕР
    ответ,множитель:ШИРВЕЩ;
  УКАЗ
    ответ:=1;
    множитель:=10;
    ПОКА x > 0 ВЫП
      ЕСЛИ НЕ ЧЕТ(x) ТО ответ:=ответ*множитель КОН;
      x:=x ДЕЛИТЬ 2;
      ЕСЛИ x > 0 ТО (* защита <множитель> от переполнения *)
        множитель:=множитель*множитель 
      КОН
    КОН;
    ВОЗВРАТ ответ
  КОН ДесятьВ;

(******************************************************************************)
  ЗАДАЧА ЗнакВЦел(знак:ЗНАК; система16:КЛЮЧ):ЦЕЛ;
  УКАЗ 
    ЕСЛИ ЗнакЦифра(знак) ТО
      ВОЗВРАТ ВЦЕЛ(знак)-ВЦЕЛ("0")
    АЕСЛИ система16 И ("A" <= знак) И (знак <= "F") ТО
      ВОЗВРАТ ВЦЕЛ(знак)-ВЦЕЛ("A")+10
    ИНАЧЕ
      ошибка:=ВКЛ
    КОН
  КОН ЗнакВЦел;

(******************************************************************************)
УКАЗ
  вОтвет:=0;
  цОтвет:=0;
  ошибка:=ОТКЛ;
  знак:=БудетЗнак(поток);
  (* пропускаем пробелы перед первой цифрой *)
  ПОКА знак <= " " ВЫП
    ЕСЛИ Конец(поток) ТО
      (* ошибка: достигнут конец потока *)
      ВОЗВРАТ ВКЛ
    КОН;
    ЧитатьЗнак
  КОН;

  знакМант:=1;
  ЕСЛИ  знак = "-" ТО
    знакМант:=-1;
    ЧитатьЗнак
  АЕСЛИ знак = "+" ТО
    ЧитатьЗнак
  АЕСЛИ НЕ ЗнакЦифра(знак) ТО
    (* ошибка: не нашли числа *)
    ВОЗВРАТ ВКЛ
  КОН;
  (* читаем мантиссу *)
  i:=0; m:=0; n:=0; d:=0;
  КОЛЬЦО 
    ЕСЛИ ЗнакЦифра(знак) ИЛИ (d = 0) И ("A" <= знак) И (знак <= "F") ТО
      ЕСЛИ (m > 0) ИЛИ (знак # "0") ТО (* предстоящие нули пропускаем *)
        ЕСЛИ n < РАЗМЕР(цифры) ТО
          цифры[n]:=знак;
          УВЕЛИЧИТЬ(n)
        КОН;
        УВЕЛИЧИТЬ(m)
      КОН;
      ЧитатьЗнак;
      УВЕЛИЧИТЬ(i)
    АЕСЛИ знак = "," ТО
      ЧитатьЗнак;
      ЕСЛИ d = 0 ТО (* i > 0 *)
        d:=i
      ИНАЧЕ
        (* ошибка: неверный знак в записи числа *)
        ВОЗВРАТ ВКЛ
      КОН
    ИНАЧЕ
      ВЫХОД
    КОН
  КОН; (* 0 <= n <= m <= i, 0 <= d <= i *)
  ЕСЛИ d = 0 ТО (* целое *)
    ЕСЛИ n = m ТО
      i:=0;
      ЕСЛИ (знак = "H") ИЛИ (знак = "X") ТО (* 16-тиричное *)
        ЧитатьЗнак;
        ЕСЛИ (n > 16) ИЛИ ((n = 16) И (цифры[0] > "7")) ТО
          (* ошибка: слишком большое целое *)
          ВОЗВРАТ ВКЛ
        КОН;
        ПОКА i < n ВЫП
          цОтвет:=цОтвет*10H+ЗнакВЦел(цифры[i],ВКЛ);
          ЕСЛИ ошибка ТО
            (* ошибка: неверный знак в записи числа *)
            ВОЗВРАТ ВКЛ
          КОН;
          УВЕЛИЧИТЬ(i)
        КОН
      ИНАЧЕ (* 10-тичное *)
        ПОКА i < n ВЫП
          d:=ЗнакВЦел(цифры[i],ОТКЛ);
          ЕСЛИ ошибка ТО
            (* ошибка: неверный знак в записи числа *)
            ВОЗВРАТ ВКЛ
          КОН;
          УВЕЛИЧИТЬ(i);
          ЕСЛИ цОтвет > (МАКС(ШИРЦЕЛ)-d) ДЕЛИТЬ 10 ТО
            (* ошибка: слишком большое целое *)
            ВОЗВРАТ ВКЛ
          КОН;
          цОтвет:=цОтвет*10+d
        КОН
      КОН
    ИНАЧЕ
      (* ошибка: очень большое число *)
      ВОЗВРАТ ВКЛ
    КОН;
    цОтвет:=знакМант*цОтвет;
    ВОЗВРАТ ВКЛ
  ИНАЧЕ (* дробная часть *)
    мант:=0; эксп:=0;
    ПОКА n > 0 ВЫП (* 0 <= мант < 1 *)
      УМЕНЬШИТЬ(n);
      мант:=(ЗнакВЦел(цифры[n],ОТКЛ)+мант)/10;
      ЕСЛИ ошибка ТО
        (* ошибка: неверный знак в записи числа *)
        ВОЗВРАТ ВКЛ
      КОН
    КОН;
    ЕСЛИ (знак = "E") ИЛИ (знак = "D") ТО
      ЧитатьЗнак;
      знакЭксп:=1;
      ЕСЛИ знак = "-" ТО
        знакЭксп:=-1;
        ЧитатьЗнак
      АЕСЛИ знак = "+" ТО
        ЧитатьЗнак
      КОН;
      ЕСЛИ ЗнакЦифра(знак) ТО
        ПОВТОРЯТЬ
          n:=ЗнакВЦел(знак,ОТКЛ);
          ЧитатьЗнак;
          ЕСЛИ эксп <= (МАКС(ШИРЦЕЛ)-n) ДЕЛИТЬ 10 ТО
            эксп:=эксп*10+n
          ИНАЧЕ
            (* ошибка: очень большое число *)
            ВОЗВРАТ ВКЛ
          КОН
        ДО НЕ ЗнакЦифра(знак);
        эксп:=знакЭксп*эксп
      ИНАЧЕ
        (* ошибка: неверный знак в записи числа *)
        ВОЗВРАТ ВКЛ
      КОН
    КОН;
    УМЕНЬШИТЬ(эксп,i-d-m); (* сдвиг десятичной точки *)
    ЕСЛИ эксп < 0 ТО
      ЕСЛИ эксп <= -307 ТО
        (* ошибка: очень маленькое число *)
        ВОЗВРАТ ВКЛ
      КОН;
      вОтвет:=мант/ДесятьВ(-эксп)
    ИНАЧЕ
      ЕСЛИ эксп > 308 ТО
        (* ошибка: очень большое число *)
        ВОЗВРАТ ВКЛ
      КОН;
      вОтвет:=мант*ДесятьВ(эксп)
    КОН
  КОН;
  вОтвет:=знакМант*вОтвет;
  ВОЗВРАТ ОТКЛ
КОН Целое;

(******************************************************************************)
ЗАДАЧА ШирВещ-(поток:Поток):ШИРВЕЩ;
ПЕР
  ширцел:ШИРЦЕЛ;
  ширвещ:ШИРВЕЩ;
УКАЗ
  ЕСЛИ Целое(поток,ширцел,ширвещ) ТО
    ВОЗВРАТ ширцел
  ИНАЧЕ
    ВОЗВРАТ ширвещ
  КОН
КОН ШирВещ;

(******************************************************************************)
ЗАДАЧА Вещ-(поток:Поток):ВЕЩ;
УКАЗ
  ВОЗВРАТ УЗК(ШирВещ(поток))
КОН Вещ;

(******************************************************************************)
ЗАДАЧА ШирЦел-(поток:Поток):ШИРЦЕЛ;
ПЕР
  ширцел:ШИРЦЕЛ;
  ширвещ:ШИРВЕЩ;
УКАЗ
  ЕСЛИ Целое(поток,ширцел,ширвещ) ТО
    ВОЗВРАТ ширцел
  ИНАЧЕ
    ВОЗВРАТ ВШИРЦЕЛ(ширвещ)
  КОН
КОН ШирЦел;

(******************************************************************************)
ЗАДАЧА Цел-(поток:Поток):ЦЕЛ;
УКАЗ
  ВОЗВРАТ УЗК(ШирЦел(поток))
КОН Цел;

КОН Читать.
