// $Source$ // $Revision$ // $Date$ ************************************ ************************************ ************ Упорядоченное хранилище клешей и операции с ним ************* $use Access Apply Arithm Box Compare StdIO Table; $use "rfp_const"; $use "rfp_vars"; $use "rfp_list"; $use "rfp_helper"; $use "rfp_compile"; /* * Собственно, хранилище. */ $box Clashes; /* * Хранятся в нём клеши в следующем формате: * * t.clash ::= (s.idx (e.Re) (s.dir e.Pe) e.boxes) * * e.boxes -- список ящиков, обозначающих свойства данного клеша. В этих * ящиках находятся номера клешей, обладающих заданным свойством и, возможно, * некая дополнительная информация для каждого клеша. * * * Ящики бывают следующие. * */ $box Ready-Source; /* * Хранит просто номера клешей. Означает, что правая часть клеша -- * переменная, либо константное выражение, следовательно, над ней можно * производить разные операции, такие как взятие подвыражений, отщепления, * всевозможные проверки и т.п. * */ $table Hard-Parts; $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, <"/=" (s.diff) (0)>, : t.var = { <"<" (s.diff) (0)> = <"*" s.diff -1> (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 Put-In e.clashes = ; // президент такой был ;-) Init-Clashes e.clashes = , , , , , , , , , ; Put-In { (e.Re) (s.dir e.Pe) e.rest = /* */ :: s.idx, /* */ :: e.boxes1, /* * Если e.R1 -- переменная или константа, кладём его в ящик * &Ready-Source. */ { \{ ; e.Re : (STATIC e); e.Re : (REF e); } = , &Ready-Source;; } :: e.boxes2, /* * Распихав клеш по всем нужным ящикам, сохраняем его в таблице вместе * со ссылками на них. */ , ; /*empty*/ = /*empty*/; }; $func Compose-Clashes e.clashes = e.clashes; Compose-Clashes { (e.Re) (s.dir e.Pe) e.rest = :: s.idx, :: e.boxes, (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); 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 ; /*empty*/ = (e.conds e.assigns); t.unready-clash e.rest = t.unready-clash ; }; UHP-Clash s.dir s.fun s.l s.r (e.conds) (e.assigns) (e.pos) (e.Re) e.Pe, { : t.Pt, { : e.len (), { t.Pt : (PAREN expr) = : t.var, , ((SYMBOL? e.Re (s.dir e.pos))) ((DEREF t.var e.Re (s.dir e.pos))) (t.var) (s.dir expr); , () () /*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; Prepare-Source t.var = t.var; Compose-Source-For-Deref = (); Get-Cycle = : e (s.idx (e.Re) (s.dir e.Pe) e.b1 &Unknown-Lengths e.b2) e.rest = :: t.var, : e.len (), : (e.left) (e.right) e.expr, s.dir : { LEFT = e.expr : t.var-e1 e.Pe-rest, t.var-e1 "lsplit_" e.Pe-rest; RIGHT = e.expr : e.Pe-rest t.var-e1, t.var-e1 "rsplit_" e.Pe-rest; } :: t.var-e1 s.pref-e2 e.Pe-rest, { = e.Pe-rest (); : t.var-e2, t.var-e2 ((t.var-e2) (s.dir e.Pe-rest)); } : t.var-e2 (e.clash), { = (Flat? True);; } :: e.flat?, , , e.rest>, :: e.clashes, >)> : e, (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 (); (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; };