Logo

21 ноября 2014 г.

Этюд 2. Частичное применение

Со времен Turbo Vision в Turbo Pascal были записи WordRec и LongRec для приведения типов. С появлением Delphi они перекочевали в модуль SysUtils, но смысл сохранился. Поскольку в практической разработке без прямого доступа к байтам и словам порой никак, некий аналог должен быть и в Канторе, и он сейчас послужит нам этюдом с частичным применением.

На Delphi

Задача типов WordRec и LongRec — подружить строгую типизацию с низкоуровневым доступом к байтам и словам. Возьмем вариант из модуля CoreUtils.
type
  WordRec = packed record
    case Byte of
      0: (Lo, Hi: Byte);
      1: (Bytes: array [0..1] of Byte);
  end;

  LongRec = packed record
    case Byte of
      0: (Lo, Hi: Word);
      1: (Words: array [0..1] of Word);
      2: (Bytes: array [0..3] of Byte);
  end;

На Канторе

Пример на Канторе адаптирован из разрабатываемого кода, в нем демонстрируется использование ключевого слова partial — оператора частичного применения.
public class Core:Typecast with inner of
  class ByteArray with inner of
    final class Bytes = 0..1;
    public out Core:Byte RawBytes[Bytes];
    public RawBytes[Core:Byte; Bytes];
  end;

  class ShortWordArray with inner of
    final class Bytes = 0..3;
    public out Core:Byte RawBytes[Bytes];
    public RawBytes[Core:Byte; Bytes];

    final class ShortWords = 0..1;
    public out Core:ShortWord RawShortWords[ShortWords];
    public RawShortWords[Core:ShortWord; ShortWords];
  end;

  class ShortWord from ByteArray of // аналог WordRec
    public LowNibble = partial RawBytes[0];
    public HighNibble = partial RawBytes[1];
  end;

  class LongWord from ShortWordArray of // аналог LongRec
    public LowNibble = partial RawShortWords[0];
    public HighNibble = partial RawShortWords[1];
  end;
end;
В сравнении с Delphi код вышел объемней, но описание промежуточных классов на Канторе требуется для автоматического учета порядка байт на разных архитектурах. Код настоящего Core:Typecast еще сложней из-за широкого использования обобщений, и для этюда его пришлось упростить.

Без частичного применения код двух последних классов будет выглядеть так:
public class Core:Typecast with inner of
  class ShortWord from ShortWordArray of // аналог WordRec
    public LowNibble = RawBytes[0];
    public LowNibble[Core:Byte value] of
      RawBytes[0] = value;
    end;

    public HighNibble = RawBytes[1];
    public HighNibble[Core:Byte value] of
      RawBytes[1] = value;
    end;
  end;

  class LongWord from ShortWordArray of // аналог LongRec
    public LowNibble = RawShortWords[0];
    public LowNibble[Core:ShortWord value] of
      RawShortWords[0] = value;
    end;

    public HighNibble = RawShortWords[1];
    public HighNibble[Core:ShortWord value] of
      RawShortWords[1] = value;
    end;
  end;
end;
Две строчки с частичным применением против восьми при расписывании вручную, — префикс partial одной строкой описывает сразу несколько свойств — все, к которым подходит частичное применение. Рутина сокращается, код становится чище и наглядней.

Подсветка синтаксиса

Публикация скриншотов с подсветкой синтаксиса Кантора становится традицией этюдов.
Добавлена подсветка синтаксиса, скриншоты удалены.

Этюд 1. WideParamStr — итераторы с охраной

Замена циклов на итераторы с охраной принята окончательно и находит постепенное воплощение в коде. В этом этюде сделана попытка переписать на Кантор функцию WideParamStr из CoreUtils.

На Delphi

type
  TWideParamRec = record
    NextParam, Param: PWideChar;
    Length: Integer;
    Quoted: Boolean;
  end;

function WideParamStr(CommandLine: PWideChar): TWideParamRec;
var
  P: PWideChar;
  L: Integer;
begin
  if CommandLine <> nil then
  begin
    while (CommandLine^ = WideChar(32)) or (CommandLine^ = WideChar(9)) do
      Inc(CommandLine);
    if CommandLine^ = WideChar('"') then
    begin
      Inc(CommandLine);
      L := WideStrLen(CommandLine);
      P := WideStrScan(CommandLine, L, CoreChar('"'));
      if P <> nil then
        L := P - CommandLine;
      Result.Quoted := True;
    end
    else
    begin
      P := CommandLine;
      while (P^ <> WideChar(32)) and (P^ <> WideChar(9)) and (P^ <> WideChar(0)) do
        Inc(P);
      L := P - CommandLine;
      Result.Quoted := False;
    end;
    with Result do
    begin
      NextParam := CommandLine + L + Byte(Quoted);
      if NextParam^ <> WideChar(0) then
        Inc(NextParam);
      Param := CommandLine;
      Length := L;
    end;
  end
  else
    FillChar(Result, SizeOf(Result), 0);
end;

На Канторе

Код не является буквальным эквивалентом собрата на Delphi: работа с локальными переменными заменена функцией-замыканием, чтобы сделать код более функциональным. Также введено ключевое слово outside для операции невхождения в набор (раньше она условно обозначалась сочетанием not in).
final class WideParamRec public of
  var ref Unsafe:WideChar NextParam, Param;
  var Integer Length;
  var Boolean Quoted;
end;

WideParamRec WideParamStr[ref Unsafe:WideChar commandLine] of
  if commandLine! then
    var ref cmdLine = commandLine;

    result[Integer len; Boolean quot] of // замыкание
      nxt = cmdLine + len + quot.AsByte;
      return new of
        NextParam = nxt + ?nxt.First{#quot, 1};
        Param = cmdLine;
        Length = len;
        Quoted = quot;
      end;
    end;

    cmdLine++ where cmdLine.First in {9, 32};
    if cmdLine.First = #quot.AsShortWord then
      cmdLine++;
      with
        len = cmdLine.StrLen;
        ptr = cmdLine.StrScan(#quot, len);
      return
        result[?ptr!{ptr - cmdLine, len}, True];
      end;
    else
      var ref ptr = cmdLine;
      ptr++ where ptr.First outside {32, 9, 0};
      return result[ptr - cmdLine, False];
    end;
  else
    return new from null;
  end;
end;
Планируется, что смысл where будет аналогичен SQL:
Упоминание членов охраняемого выражения обозначает итерации, а внешние по отношению к охране условия — немедленный выход, аналогичный оператору break.

Подсветка синтаксиса

Подсветки синтаксиса в блоге пока нет, а с ней этюд выглядит гораздо приятней.
Добавлена подсветка синтаксиса, скриншоты удалены.