// $Source$ // $Revision$ // $Date$ //*********************************** ************************************ //*********** Упорядоченное хранилище клешей и операции с ним ************* $use Access Apply Arithm Box Compare List StdIO Table; $use "rfp_const"; $use "rfp_vars"; $use "rfp_compile"; /* * Собственно, хранилище. */ $box Clashes; /* * Хранятся в нём клеши в следующем формате: * * t.clash ::= (s.idx (e.Re) (s.dir e.Pe) e.boxes) * * e.boxes -- список ящиков, обозначающих свойства данного клеша. В этих * ящиках находятся номера клешей, обладающих заданным свойством и, возможно, * некая дополнительная информация для каждого клеша. * * * Ящики бывают следующие. * */ /* * Образец может иметь жёсткие по длине части слева и справа. Сопоставление с * этими частями выливается в проверку условий возможности такого сопоставления * и заведение новых переменных. Всё это делается до компиляции циклической * части образца. * * В результате означивания новых переменных, жёсткие части образца могут * удлинняться. Следующая таблица по индексу клеша хранит запись вида: * (e.left) (e.right) expr. * e.left -- выражение, в ран-тайм получающее значение длины жёсткой части * слева. * e.right -- аналогичное выражение для длины жёсткой части справа. * expr -- часть выражения между жёсткими кусками. */ $table Hard_Parts; $box Parenth; $box Unready_Source; $func Add_Clash_To_Var e = e; Add_Clash_To_Var t.clash t.var = t.clash) t.var>; $func Classify_Lengths t.clash = e.boxes; Classify_Lengths (s.idx (e.Re) (s.dir e.Pe) e) = :: e.len_Re (e.vars_Re), :: e.len_Pe (e.vars_Pe), { /* * Если длины всех переменных на верхних уровнях e.Re и e.Pe * известны, кладём клеш в ящик &Known-Lengths. */ e.vars_Re : /*empty*/, e.vars_Pe : /*empty*/ = , &Known_Lengths; /* * Если на верхнем уровне во всём клеше ровно одна переменная с * неизвестной длинной, и она входит в левую и правую части разное * кол-во раз, то её длину можно вычислить. * В каждой переменной делаем пометку, что она используется в этом клеше. * Кладём клеш в ящик &Compute-Length. */ > :: s.diff, , : t.var = { = (e.len_Re) (e.len_Pe); s.diff (e.len_Pe) (e.len_Re); } :: s.mult (e.minuend) (e.subtrahend), : e, , &Compute_Length; /* * В оставшихся случаях, всё, что мы можем сделать -- выписать * граничные условия и ждать, пока не появится новой информации о длине * каких-либо переменных. * В каждой переменной делаем пометку, что она используется в этом клеше. * Кладём клеш в таблицу &Unknown-Lengths. */ : e, , &Unknown_Lengths; }; /* * Может оказаться, что клеша с номером s.idx уже нет в хранилище -- из памяти * переменных клеши не удаляются. В этом случае просто не надо ничего делать. */ Reclassify_Clash s.idx, { : e1 (s.idx (e.Re) (s.dir e.Pe) e.boxes) e2, :: e.boxes1, , , :: e.boxes2, ;; }; //********************* Индекс для нумерации клешей *********************** $box FreeIdx; $func Free_Index = s.idx; Free_Index = : s.idx, >, s.idx; //**************************** Инициализация ****************************** $func Compose_Clashes e.clashes = e.clashes; Init_Clashes e.clashes = , , , , , , , , , >; Compose_Clashes { (e.Re) (s.dir e.Pe) e.rest = :: s.idx, :: e.boxes, { \{ : True; e.Re : (REF e); e.Re : (STATIC e); }; e.Pe : e1 (PAREN e) e2 = :: e.Re_vars, )>, { e2 : $r e (PAREN e) e3 = )>;; }, ; )>; }, (s.idx (e.Re) (s.dir e.Pe) e.boxes) ;; }; //****** Обновление информации о жёстких началах и концах образцов ******** $func UHP (e.conds) (e.assigns) e.clashes = e.clashes (e.actions); $func UHP_Clash s.dir s.fun s.l s.r (e.conds) (e.assigns) (e.pos) (e.Re) e.Pe = e.clashes (e.conds) (e.assigns) (e.pos) (e.Pe); /* * Просматриваем все имеющиеся клеши. * Если в результате новой информации о переменных, входящих в образец, можно * утверждать что жёсткие части удлиннились, запоминаем эту информацию в * таблице &Hard-Parts, заводим новые клеши, получающиеся из скобок в образце, * и возвращаем условия и присваивания, нужные, чтобы завести эти клеши. * Новые, входящие в жёсткие, части образца, не являющиеся скобками, кладутся в * ящик &Eqs вместе с результатным выражением и с позицией в этом выражении с * которой надо эти части сопоставлять. */ Update_Hard_Parts = > :: e.clashes (e.actions), , e.actions; UHP (e.conds) (e.assigns) e.clashes, e.clashes : { t.clash e.rest, t.clash : (s.idx (t.Re) (s.dir e.Pe) e), \{ : True; t.Re : (REF e); t.Re : (STATIC e); } = { ; (0) (0) e.Pe; } : (e.left) (e.right) expr, :: e.l_clashes (e.l_conds) (e.l_assigns) (e.left ) (expr), :: e.r_clashes (e.r_conds) (e.r_assigns) (e.right) (expr), , t.clash ; t.unready_clash e.rest = t.unready_clash ; /*empty*/ = (e.conds e.assigns); }; /* * Функция, занимающаяся непосредственно проверкой составляющих образца на * вычислимость длин, начиная слева или справа, в зависимости от s.dir. * * Если очередной терм -- это скобки, то должен быть заведён новый клеш, * образованный из содержимого скобок. Перед этим надо произвести проверку на * то, что в результатном выражении в этом месте тоже стоят скобки, и завести * переменную, обозначающую их содержимое. * Данная функция возвращает всю информацию, необходимую для этих действий. */ UHP_Clash s.dir s.fun s.l s.r (e.conds) (e.assigns) (e.pos) (e.Re) e.Pe, { e.Pe : v, : t.Pt, { : e.len (), { t.Pt : (PAREN expr) = : t.var, , { s.fun : &R = RIGHT e.pos 1; LEFT e.pos; } :: e.pos, (("SYMBOL?" e.Re (e.pos))) ((DEREF t.var e.Re (e.pos))) (t.var) (s.dir expr); { s.fun : &R = ; ; }, () () /*empty*/; } :: (e.cond) (e.assign) e.clash = e.clash >; (e.conds) (e.assigns) (e.pos) (e.Pe); }; (e.conds) (e.assigns) (e.pos) (); }; $func Prepare_Source e.source = t.var e.assign; $func Define_Vars e.vars = e.eqs; Prepare_Source { t.Re, \{ : True; t.Re : (REF e); t.Re : (STATIC e); } = t.Re /*empty*/; t.Re, = t.Re ; e.Re = : t.var, , : e, // ??? t.var > (DECL Expr ) (ASSIGN e.Re); }; Define_Vars { t.var e.rest = { : True = ; : e1 (t.Re t.pos t.var t.len) e2 = , (t.Re t.pos t.var t.len) ; }; /*empty*/ = /*empty*/; }; $func Find_SFD e.parenth = e.parenth (e.idx); $func? Not_Instantiated_Var e = e; $func? Not_Idx e = e; Not_Idx { s.idx (s.idx e) = $fail; e.else_true; }; Compose_Source = \{ > : e.parenth (s.idx) = , )>>, s.idx; : e.l (s.idx e.vars) e.r, : /*empty*/ = , )>>, s.idx; } :: s.idx, { : e1 (s.idx (e.Re) (s.dir e.Pe) e.boxes) e2, :: t.var e.assign, , e.assign; }; Find_SFD { (s.idx (e.Re_vars) e.Pe_vars) e.rest = : { v.r_vars = (s.idx (v.r_vars) e.Pe_vars) ; /*empty*/ = : { e (v.p_vars) = (s.idx () v.p_vars) ; e () = (s.idx); }; }; /*empty*/ = (); }; Not_Instantiated_Var t.var = # \{ : True; : e (t t t.var t) e; }; Get_Cycle = : e (s.idx (t.var) (s.dir e.Pe) e.b1 &Unknown_Lengths e.b2) e.rest = : e.len (), : (e.left) (e.right) e.expr, s.dir : { LEFT = e.expr : t.var_e1 e.Pe_rest, LSPLIT t.var_e1 "lsplit_" e.Pe_rest; RIGHT = e.expr : e.Pe_rest t.var_e1, RSPLIT t.var_e1 "rsplit_" e.Pe_rest; } :: s.split t.var_e1 s.pref_e2 e.Pe_rest, { = e.Pe_rest ((e.Pe_rest) (s.dir )); // ; : t.var_e2, t.var_e2 ((t.var_e2) (s.dir e.Pe_rest)); } : t.var_e2 (e.clash), , , e.rest>, :: e.clashes, >)> : e, s.split (e.left) (e.right) (e.len) t.var t.var_e1 t.var_e2; $func Ref_Len t.name = e.length; /* * Из верхнего уровня выражения изымаются все переменные, длина которых не * может быть посчитана (она неизвестна из формата, и переменная ещё не * получила значение в run-time). Список этих переменных возвращается вторым * параметром. Первым параметром возвращается длина оставшегося после их * изъятия выражения. */ Get_Known_Length e.Re = e.Re (/*e.length*/) (/*e.unknown-vars*/) $iter { e.Re : t.Rt e.rest, t.Rt : { s.ObjectSymbol = 1 (); // Может появиться из константы. (PAREN e) = 1 (); (FUNC e) = 1 (); ("FUNC?" e) = 1 (); (REF t.name) = (); (STATIC t.name) = >; t, , { : v.len = v.len (); /*empty*/ (t.Rt); }; } :: e.len (e.var), e.rest (e.length e.len) (e.unknown_vars e.var); } :: e.Re (e.length) (e.unknown_vars), e.Re : /*empty*/ = { e.length : /*empty*/ = 0 (e.unknown_vars); e.length (e.unknown_vars); }; $table Const_Len; // Fixme: инициализировать когда? Ref_Len t.name = { ; >> :: e.len t = , e.len; 1; };