12.5 Подключение объектов — Возвращаемся к value

Предыдущая  Содержание  Следующая V*D*V

Запись и чтение автономного набора со строгой древовидной структурой объектов может быть выполнено с помощью puto(), retrieve() и соответствующих методов geto. Наш калькулятор демонстрирует, что появляется проблема, как только записан набор объектов, который ссылается на другие объекты, записанные в различном контексте или не записанные вообще. Рассмотрим:

 

$ value

def sqr = $ * $

def one = sqr(sin($)) + sqr(cos($))

save one

 

Выходной файл one.sym содержит ссылки на sqr, но не определение:

 

$ cat one.sym

Fun at 0x50ec9f8

    name one

    lex 102

    value 10

=

Add at 0x50ed168

User at 0x50ed074       Ref

    name sqr            putsymbol

    lex 102

Builtin at 0x50ecfd0    Ref

    name sin            putsymbol

    lex 109

Parm at 0x50ecea8       Val

    name one            putsymbol

    lex 102

User at 0x50ed14c

    name sqr

    lex 102

Builtin at 0x50ed130

    name cos

    lex 109

Parm at 0x50ed118

    name one

    lex 102

 

User представляет собой Ref, и Ref_puto() использовал putsymbol() в parse.c, чтобы записать только имя символа и значение маркера. Таким образом, определение для sqr() не сохранено в one.sym умышленно.

Как только ссылка таблицы символов считана, она должна быть присоединена к таблице символов. Наш калькулятор содержит единственную таблицу символов table, которая создаётся и управляется в parse.c, то есть ссылка из дерева выражения на таблицу символов должна использовать getsymbol() из parse.c, чтобы присоединить ссылку к текущей таблицу символов. Чтобы надлежащий подкласс Symbol мог быть найден или создан getsymbol(), разные виды ссылок используют разные подклассы Node. Поэтому необходимо различать Global как ссылку на Var, а Parm как ссылку на Fun там, где извлекается значение параметра.

 

% Global geto {

    struct Global * self = super_geto(Global(), _self, fp);

 

    down(self) = getsymbol(Var(), fp);

    return self;

}

 

% Parm geto {

    struct Parm * self = super_geto(Parm(), _self, fp);

 

    down(self) = getsymbol(Fun(), fp);

    return self;

}

 

Точно так же Assign ищет Var; Builtin ищет Math; и User ищет Fun. Все они используют getsymbol(), чтобы найти подходящий символ в table, создать новый или пожаловаться, если существует символ с правильным именем, но неправильным классом:

 

void * getsymbol (const void * class, FILE * fp) {

    char buf [BUFSIZ];

    int token;

    void * result;

 

    if (fscanf(fp, "\tname %s\n\tlex %d\n", buf, & token) != 2)

        assert(0);

    result = screen(table, buf, UNDEF);

    if (lex(result) == UNDEF)

        install(table, result =

                            new(class, name(result), token));

    else if (lex(result) != token) {

        fclose(fp);

        error("%s: need a %s, got a %s",

                buf, nameOf(class), nameOf(classOf(result)));

    }

    return result;

}

 

Помогает то, что когда символ Fun создан, ещё нет необходимости определять выражение:

 

$ value

load one

one(10)

undefined function

 

one() пробует вызвать sqr(), но он не определён.

 

let sqr = 9

bad assignment

 

Не определённый Symbol может быть перезаписан и присвоен, то есть sqr() действительно не определённая функция.

 

def sqr = $ * $

one(10)

        1

def sqr = 1

one(10)

        2

 

Вот иерархия классов калькулятора с большинством определений методов. Метаклассы были опущены; жирный шрифт указывает, где метод определён изначально.

 

КЛАСС

ДАННЫЕ

МЕТОДЫ

Object

magic, ...

%  classOf, ...

 

 

%- delete, puto, geto, ...

 

 

%+ new

  Node

 

%  sunder

 

 

%- delete, exec

 

 

%+ new, reclaim

    Number

value

%- ctor, puto, geto, exec

    Monad

down

%- ctor

      Val

 

%- puto, exec

        Global

 

%- geto

        Parm

 

%- geto

      Unary

 

%- dtor, puto, geto

        Minus

 

%- exec

    Dyad

left, right

%- ctor

      Ref

 

%- dtor, puto

        Assign

 

%- geto, exec

        Builtin

 

%- geto, exec

        User

 

%- geto, exec

     Binary

 

%- dtor, puto, geto

        Add

 

%- exec

        Sub

 

%- exec

        Mult

 

%- exec

        Div

 

%- exec

  Symbol

name, lex

%  name, lex

 

 

%- ctor, puto, geto

    Reserved

 

%- delete

    Val

value

%  value, setvalue

 

 

%- puto, geto, move

     Const

 

%- ctor, delete, move

     Fun

fun

%  setfun, funvalue

 

 

%- puto, geto, move

    Math

fun

%  mathvalue

 

 

%- ctor, delete

  Symbol

value, ...

%  save, load, ...

 

 

%- ctor, puto, delete

 

Остаётся незначительный пробел, к которому обратимся в следующей главе: по-видимому, getsymbol() знает достаточно, чтобы закрыть поток fp до использования error(), чтобы вернуться к основному циклу.

 

Предыдущая  Содержание  Следующая