// $Source$ // $Revision$ // $Date$ $use "rfpc"; $use "rfp_helper"; $use "rfp_check"; $use "rfp_as2as"; $use "rfp_format"; $use "rfp_vars"; $use "rfp_const"; $use "rfp_clashes"; $use StdIO; $use Table; $use Box; $use Arithm; $use Access; $use Compare; $use Convert; $use Class; $use Apply; $use Dos; $use List; /* * Table for storing object names. */ $table Objects; /* * Table for storing parameters of referenced functions. */ $table Stub_Funcs; /* * Box for storing function out format */ $box Out_Format; /* * Box for storing names for function result variables */ $box Res_Vars; /* * Following table is used by Gener-Label function for obtaining unical (for * certain function) label name. * e.Key ::= e.QualifiedName (parameter given to Gener-Label) * e.Val ::= [Int] (last index used with such e.QualifiedName) */ $table Labels; /* * Table for storing variables used in place of preprocessor-generated ones. */ $table Prep_Vars; $func Compile (e.targets) e.Items = e.Compiled_Items; $func Comp_Func_Stubs = e.asail_funcs; $func Comp_Func s.linkage s.tag t.name e.params_and_body = e.compiled_func; $func Comp_Sentence e.Sentence = e.asail_sentence; $func Save_Snt_State = ; $func Recall_Snt_State = ; $func Pop_Snt_State = ; $func Extract_Calls e.Re = (e.last_Re) e.calls; $func Get_Clash_Sequence (e.last_Re) e.Snt = (e.clashes) e.rest_of_the_Sentence; $func? IsWithout_Calls e.Re = ; $func Comp_Clashes (e.clashes) s.Istail (v.fails) e.Sentence = e.asail_sentence; $func Gener_Label e.QualifiedName = t.label; $func Add_To_Label t.label e.name = t.label; $func Comp_Calls e.Re = e.calls; $func Prepare_Vars e.vars = e.vars; $func Prepare_Res e.Reult_exprs = e.Result_exprs; $func Prepare_Const e.const_expr = e.const_expr; $func Comp_Assigns e.assignments = e.asail_assignments; $func Comp_Format (e.last_Re) e.He = e.assignments; //*********** Get AS-Items and targets, and pass it to Compile ************ /* * Ящик для объявлений статических функций, констант и объектов. Все они * выписываются в самом начале тела модуля. */ $box Declarations; $box Trace_Names; $box Module_Name; RFP_Compile (e.ModuleName) e.Items = , { ;; } :: e.targets, , , , , :: e.Items, :: e.stub_funcs, (MODULE (e.ModuleName) e.Items e.stub_funcs); //***************** Choose needed items and compile them ****************** Compile (e.targets) e.Items = e.Items (/*e.asail*/) $iter { e.Items : t.item e.rest, { e.targets : v = e.targets : e t.name e, t.item : (t t t t.name e);; }, t.item : { (IMPORT s.tag t.pragma t.name e) \? { t.pragma : (PRAGMA e (FILE e.fname) e), s.tag : \{ CONST; FUNC; "FUNC?"; }, e.fname : RFI "org" "refal" "plus" "wrappers" e \! $fail;; }; (TRACE t.name) = ; (EXTERN t.pragma t.name) = ; (s.link s.tag t.pragma t.name (e.in) (e.out) e.body), FUNC "FUNC?" TFUNC : e s.tag e = { e.body : (BRANCH t.p e.branch) = >; ) (e.out)>; }; (s.link CONST t.pragma t.name e.expr) = )>; (s.link s.tag t.pragma t.name) = ; } :: e.item, e.rest (e.asail e.item); } :: e.Items (e.asail), e.Items : /*empty*/ = e.asail; $func Gener_Stub e = e; /* * For each referenced function generate a stub one with format e = e. */ Comp_Func_Stubs = )>; Gener_Stub (t.name) = : t.stub_name s.tag (e.Fin) (e.Fout), :: e.He, ; Comp_Func s.linkage s.tag t.name (e.in) (e.out) e.Sentence = , , , > :: e.res_vars, : e, , >, : (e.arg), :: e.arg_vars, : e, s.tag : { FUNC = FUNC (FATAL); "FUNC?" = "FUNC?" (RETFAIL); TFUNC = TFUNC (FATAL); STUB = : (e.message), "FUNC?" (RETFAIL) ((ERROR e.message)); } :: s.tag e.fails, (s.tag s.linkage t.name () () ) :: e.comp_func, :: e.comp_func, //! :: t e.result, //! e.result; e.comp_func; // :: (e.func-decl) e.func-body, // () $iter { // e.vars : (t.var) e.rest-vars, // (e.var-decls (DECL t.var)) e.rest-vars; // } :: (e.var-decls) e.vars, // e.vars : /*empty*/, // (e.func-decl e.var-decls e.func-body); Comp_Sentence s.Istail (v.fails) (e.last_Re) e.Sentence, e.Sentence : { /*empty*/ = /*empty*/; /* * In case of Re look if we should do a tailcall. If not, then compile * function calls from the Re and assign results to the out parameters or * use them in compilation of the rest of the sentence. */ (RESULT e.Re) e.Snt = { /* * If the Re is the last action in the sentence then we can do * tailcall if one of the following is true: * - Re is a call of non-failable function; * - Re is a call of a failable function, current function is * failable, and the failures stack is empty. * In both cases out format of the called function should coincide * with those of compiled one. * FIXME: really we can do tailcall if all the parameters of * compiled function that won't get their values from the call can * be assigned from other sources. Some support from runtime is * needed though. */ e.Snt : /*empty*/, s.Istail : Tail, e.Re : (CALL t.name e.arg), { = v.fails : (RETFAIL), "TAILCALL?"; TAILCALL; } :: s.tailcall, :: s.linkage s.tag t.pragma (e.Fin) (e.Fout), )> = :: (e.last_Re) e.calls, > :: e.splited_Re, e.calls> (s.tailcall t.name (e.splited_Re) ()); :: (e.last_Re) e.calls, e.calls> :: e.comp_calls, { e.Snt : /*empty*/, Tail "Tail-in-Trap" : e s.Istail e = ) e.last_Re> :: e.splited_Re, :: e.splited_Re, e.comp_calls ) (e.splited_Re)>>; e.comp_calls ; }; }; /* * In case of He compile assignments from last Re and then (with new state * of variables) proceed with the rest of the sentence. */ (FORMAT e.He) e.Snt = ; /* * In case of Pe get from the begining of the sentence a maximum possible * sequence of clashes and compile it. New values of variables from the * clashes use in the compilation of the rest of the sentence. */ (s.dir e.Pattern) e.Snt, s.dir : \{ LEFT; RIGHT; } = :: (e.clashes) e.Sentence, ; ("BLOCK?") e = v.fails : e (e.last_fail), e.last_fail; (BLOCK) e = FATAL; /* * In case of a block first see if its results are needed for something * after the block and determine whether the block is a source. Then * compile each branch in turn. */ (s.block e.branches) e.Snt, s.block : \{ BLOCK = (FATAL); "BLOCK?"; } :: e.Isfatal = /* * If the block initializes an $iter then extract from the $iter the He * for placing it in the end of each branch. * Then look if the block is used by a format expression. * If so, we should declare variables from that expression before * entering any branch -- those should be visible after the block. * The format expression is placed in the end of each branch. * But if a branch computes to $error, the expression shouldn't be * used, so protect it with (Comp If-not-error). * If next after the block is (Comp Error) then block results should be * used as values for $error, so place (Comp Error) in the end of each * branch. * If next after the block is (Comp If-not-error) then our block is in * the end of a branch of an outer block and has next pattern or format * inherited from there. In that case we should place all the sentence * rest in the end of each branch because the block can be inside the * $error already. */ { e.Snt : (ITER t.body t.format t.cond) e.rest = t.format (Comp Iter t.body t.format t.cond) e.rest; e.Snt; } :: e.Snt, e.Snt : { (FORMAT e.format) e.rest = > :: e.vars, (e.vars) ((Comp "If-not-error") (FORMAT e.format)) ((Comp Source)) e.rest; (Comp Error) e.rest = () ((Comp Error)) () /*empty*/; (Comp "If-not-error") e.rest = () (e.Snt) () /*empty*/; e = () () () e.Snt; } :: (e.out_vars) (e.next_terms) (e.Issource) e.Snt, /* * The block is a source if after it goes format expression * (in that case e.source? isn't empty) or e.Snt isn't empty. * Branches in the block are tail sentences if the current sentence is * tail and the block isn't a source. */ { \{ e.Issource : v; e.Snt : v; } = ((Comp Source) ) Notail; () s.Istail; } :: (e.Issource) s.Istail_branch, /* * In case our block is a source we should mark the position in the * failures stack, so that we can jump to it after CUTALL. And if our * block isn't failable we should add (FATAL) to the end of the stack. */ v.fails e.Issource e.Isfatal :: v.branch_fails, /* * Before compile the branches mark all out-vars as declared. */ :: e.decls, /* * We put all compiled branches in a block, so positive return from a * branch is a break from that block. * Each branch in its turn is placed in its own block, so for a $fail * to the next branch we should just break from that inner block. * Each branch is compiled with the current sentence state and the * state is recalled after that. When all branches are compiled the * state is popped out from the stack. * If last branch fails then the whole block fails, and return from the * last branch is return from the block. So the last branch isn't * placed in a block and is processed with the failures stack that was * before entering the block. Note: this trick helps us find more * tailcalls. If the call of a failable function is on the last branch * of the block and the failures stack is empty we can do tailcall. * When the last branch is compiled with the block's stack, all we * should do is to check it. */ :: t.label, , (e.branches) /*e.comp-branches*/ $iter { e.branches : (BRANCH e.branch) e.rest_br = :: t.br_label, :: e.comp_br, , (e.rest_br) e.comp_branches (LABEL (t.br_label) e.comp_br (BREAK t.label)); } :: (e.branches) e.comp_branches, e.branches : (BRANCH e.branch) = :: e.last_branch, , , e.decls (LABEL (t.label) e.comp_branches e.last_branch) ; /* * In case of $iter first of all compile initial assignment to the hard * expression. */ (ITER t.body t.format t.cond) e.Snt = ; /* * Before compiling $iter condition or body we should forget available info * about all format variables, because that info can be changed during * cycle iterations. * Then compile $iter condition and body both with the current state of the * sentence. * e.Snt can contain (Comp Error) and (protected from errors) pattern or * format which comes from an outer block, so compile it together with the * condition. * If condition fails we should compute the body, so put the compiled * condition in a block and place a break from it to the failures stack. */ (Comp Iter (BRANCH e.body) t.format (BRANCH e.condition)) e.Snt = t.format : (FORMAT e.Fe), >>, , :: t.label, :: t.exit, :: e.comp_condition, , :: e.comp_body, (FOR (/*cont-label*/) (t.exit) () () (LABEL (t.label) e.comp_condition (BREAK t.exit)) e.comp_body ); /* * In case of $trap/$with at first compile try-sentence. All $fails from * it should become errors. * Then recall the state of the sentence and compile catching of an error * with a variable err. * e.Snt can contain (Comp Error) and (protected from errors) pattern or * format which comes from an outer block, so compile it together with both * sentences. */ (TRY (BRANCH e.try) e.catch) e.Snt = , { s.Istail : Tail = "Tail-in-Trap"; s.Istail; } :: s.Istail_in_trap, :: e.comp_try, , :: t.var, , :: e.comp_catch, (TRY e.comp_try) ("CATCH-ERROR" e.comp_catch); /* * In case of \? add Stake to the failures stack. Add last fail after it * for continue to work. */ (STAKE) e.Snt = ) () e.Snt>; /* * In case of \! forget all failure catchers after last \?. * If there is no Stake then we are inside negation or error (we assume the * program is correct). So the right failure catcher is in the bottom of * the stack. */ (CUT) e.Snt = { v.fails : $r v.earlier_fails (Comp Stake) e = v.earlier_fails; ; } :: v.fails, ; /* * In case of = clear the failures stack up to the closest source. * Don't clear last fail after it for continue to work. */ (CUTALL) e.Snt = { v.fails : $r v.earlier_fails (Comp Source) t.fail e = v.earlier_fails (Comp Source) t.fail; ; } :: v.fails, ; /* * In case of = in the Refal-6 sense (non-transparent hedge for the fails), * $fail(k) should become $error(Fname "Unexpected fail"), so clear the * failures stack and put that value in it. */ NOFAIL e.Snt = ; /* * In case of $fail return last failure catcher. */ (FAIL) e.Snt = v.fails : e (e.last_fail), e.last_fail; /* * In case of # we should proceed with the rest if the source is computed * to $fail. * We could compile the rest of the sentence and place it in the * failures stack. But then the compiled sentence would be copied as many * times as there are $fail's to the upper level in the source. So we * place compiled source in the block and put the break to exit from it in * the stack. * When compiling the source mark it as Notail as usual. * If the source isn't computed to $fail we should proceed with the last * failure catcher. */ (NOT (BRANCH e.branch)) e.Snt = :: t.label, v.fails : e (e.last_fail), // , e.last_fail :: e.comp_negation, // , (LABEL (t.label) e.comp_negation) ; /* * In case of $error all fails become $error(Fname "Unexpected fail"). So * place that value in the failures stack and then compile the computation * of the rest of the sentence and the last Re which should be the value of * $error. */ (ERROR) e.Snt = ; (Comp Error) e.Snt = : (e.Re), (ERROR e.Re); /* * Protection mark to be used between source and tail. If there is $error * construction somewhere in the source then the tail shouldn't be * computed, but instead the source value should be used for throwing. */ (Comp "If-not-error") e.Snt = { e.Snt : e (Comp Error) = ; ; }; // (Comp Fatal) = FATAL; // (Comp Retfail) = RETFAIL; }; //********* Sentence state stack and functions for work with it. ********** $box Snt_State; /* * Put current state in the stack. */ Save_Snt_State = >; /* * Set current state to that at the top of the stack. */ Recall_Snt_State = >>; /* * Pop the top from the stack and set current state to it. */ Pop_Snt_State = , >>; //********************* Function calls compilation. *********************** /* * $func Extract-Calls e.Re = (e.last-Re) e.calls; * * * */ Extract_Calls { (CALL t.name e.arg) e.rest = :: s.linkage s.tag t.pragma (e.Fin) (e.Fout), :: (e.last_Re) e.calls, > :: e.splited_Re, t.name : (e s.prefix), :: e.Re, :: e.vars, : e, { s.tag : "FUNC?" = (Failable (CALL t.name (e.splited_Re) (e.vars))); (CALL t.name (e.splited_Re) (e.vars)); } :: t.call, :: (e.rest_Re) e.rest_calls, (e.Re e.rest_Re) e.calls t.call e.rest_calls; (PAREN e.Re) e.rest = :: (e.last_Re) e.calls, :: (e.rest_Re) e.rest_calls, ((PAREN e.last_Re) e.rest_Re) e.calls e.rest_calls; t.Rt e.Re = :: (e.last_Re) e.calls, (t.Rt e.last_Re) e.calls; /*empty*/ = () /*empty*/; }; Comp_Calls (e.fail) e.calls, e.calls : { (Failable t.call) e.rest = (IF ("CALL-FAILS" t.call) e.fail) ; t.call e.rest = t.call ; /*empty*/ = /*empty*/; }; //********* Preparation of vars and REs for following processing ********** //********** Compilation of static parts of result expressions ************ $func IsStatic_Expr s.Iscreate e.Re = sIstatic e.Re; $func IsRef_Func t = t; $func IsStatic_Term t.Rt = sIstatic e.Re; $func Stub_Name t.name = t.stub_name; /* * Extract static parts from each Re. * Also get the right names for variables generated during the preprocessing * stage, if those are in the expr. */ Prepare_Res { (e.Re) e.rest = :: s e.Re, (e.Re) ; /*empty*/ = /*empty*/; }; /* * Find all the longest static parts in the upper level of Re. Create STATIC * form in place of each one. * Return a tag pointing whether the whole expression is static and expression * with static parts replaced by STATIC forms. Dynamic parts are returned * unchanged. */ IsStatic_Expr s.Iscreate e.Re = (/*e.static*/) e.Re $iter { e.Re : t.Rt e.rest = : { Static e.st_Re = (e.static e.st_Re) e.rest; Dynamic t.dyn_Rt = :: s e.rest, (e.static) (Dynamic t.dyn_Rt e.rest); }; (e.static); } :: (e.static) e.Re, e.Re : \{ /*empty*/, { s.Iscreate : Create = Static ; Static e.static; }; (Dynamic e.dynamic) = Dynamic e.dynamic; }; /* * The same as Static-Expr? but for terms. */ IsStatic_Term { symbol = Static symbol; (PAREN e.Re) = :: sIstatic e.Re, sIstatic (PAREN e.Re); (REF t.name) = Static ; (STATIC t.name) = Static ; t.var = : t.prep_var, Dynamic t.prep_var; }; IsRef_Func { (REF t.name) = { : { s.linkage s.tag t.pragma ((EVAR)) ((EVAR)) = (s.tag t.name); s.linkage s.tag t.pragma (e.Fin) (e.Fout) = { : t.stub_name e = ("FUNC?" t.stub_name); :: t.stub_name, , ("FUNC?" t.stub_name); }; }; (REF t.name); }; term = term; }; /* * Обеспечивает, что сгенерированные препроцессорами переменные (с именами, * оканчивающимися на число) не пересекаются с программными переменными (за * счёт того, что таг будет VAR). */ Prepare_Vars { // (s.var-tag (e.prefix s.n)) e.rest, = // { // ; // :: e.var, // , // e.var; // } :: e.var, // e.var ; t.var e.rest = t.var ; /*empty*/ = /*empty*/; }; /* * Генерируем уникальные внутри модуля имена для функций-заглушек. */ Stub_Name (e.qualifiers s.name) = : { e1 '_' s.n, = e1 '_' ; e1 = e1 '_' 0; } :: e.name, // (e.qualifiers ) :: t.name, ( ) :: t.name, { : e = ; t.name; }; Prepare_Const { (PAREN expr) e.rest = (PAREN ) ; t1 e.rest = ; /*empty*/ = /*empty*/; }; //**************** Compilation of assignment to variables ***************** $func Comp_Assign_to_Var t.var e.Re (e.assigned_vars) = e.assign (e.used_vars); Comp_Assign_to_Var t.var e.Re (e.assigned_vars) = { t.var : e.Re = /*empty*/ (); , $fail; , # \{ e.assigned_vars : e t.var e; } = (); : s = (ASSIGN e.Re) (); : e = (DECL Expr ) (ASSIGN e.Re) (); }; Comp_Assigns e.assigns = e.assigns (/*e.assigned-vars*/) (/*e.comp-assigns*/) $iter { e.assigns : (t.var (e.Re)) e.rest = :: e.c_as (e.a_vs), e.rest (e.assigned_vars e.a_vs) (e.comp_assigns e.c_as); } :: e.assigns (e.assigned_vars) (e.comp_assigns), e.assigns : /*empty*/ = e.comp_assigns; //************************* FORMAT compilation. *************************** $box Aux_Index; $func Gener_Aux_Var = t.new_aux_var; Gener_Aux_Var = : s.n, >, (VAR ("aux" s.n)); $func Create_Aux_Vars (e.vars) e.splited_Re = e.assigns; Comp_Format (e.last_Re) e.He = > :: e.vars, ) e.last_Re>> :: e.splited_Re, , :: e.assigns, ; /* * Итак, e.vars -- все переменные, входящие в форматное выражение. Каждая * переменная может входить в форматное выражение только один раз, поэтому * повторяющихся среди них нет. * e.splited-Re -- набор результатных выражений. На каждую переменную из * e.vars по выражению, которое должно быть ей присвоено. * * Если переменная t.var_i используется в выражении e.Re_j, и i /= j, то * переменной t.var_j значение должно быть присвоено раньше, чем перeменной * t.var_i. Если же, по аналогичным соображениям, t.var_i должна получить * значение раньше t.var_j, необходимо завести вспомогательную переменную. * * Пример: * * t1 (t1 t2) (t1 t3) :: t2 t1 t3 * * t3 = (t1 + t3)(); * aux_1 = t1; * t1 = (t1 + t2)() * t2 = aux_1; * * В общем случае вспомогательная переменная требуется, если двум переменным * необходимы старые значения друг друга (возможно, не напрямую, а через * промежуточные переменные). * * Вместо того, чтобы искать и анализировать такие циклы, будем действовать по * методу "наибольшей пользы". А именно: * * - Для каждой переменной выпишем все другие переменные, которым требуется * её старое значение, а также отдельно те, старые значения которых * требуются ей. * * - Всем переменным, от старых значений которых ничего не зависит, можно * смело присвоить новые значения. При этом они исчезают из списков * зависимостей оставшихся переменных. * * - Все переменные, новые значения которых ни от чего не зависят, можно * отложить, чтобы присвоить им значения тогда, когда будет удобно. Т.е. * тогда, когда списки зависящих от них переменных опустеют. * * - Чтобы означить оставшиеся, нужны вспомогательные переменные. Выберем * одну из переменных, с максимальным списком тех, от которых она зависит, * и положим её значение во вспомогательную переменную. Так как мы сразу * уменьшили кол-во зависимостей у максимального кол-ва переменных, * локально мы добились наибольшей пользы, хотя не исключено, что глобально * такой метод и не даст наименьшего кол-ва вспомогательных переменных. * Кроме того, мы не пытаемся выбрать наилучшую переменную из нескольких с * максимальным списком зависимостей. * * - Повторяем всё это до тех пор, пока у каждой переменной не опустеет * список зависящих от неё. * * * Для нашего примера: * * t1 (t1 t2) (t1 t3) :: t2 t1 t3 * * t1 -- (t2 t3) (t2) * t2 -- (t1) (t1) * t3 -- () (t1) * * * Для каждой переменной var_i найдём все j /= i, такие что в Re_j встречается * var_i -- provide[i], и а также все j /= i, такие что var_j нужна для * подсчёта var_i, т.е. встречается в Re_i. * * Res-vars <- * for var_i in vars * provide[i] <- * for vars-Re_j in Res-vars, j /= i * vars-Re_j : e var_i e = j * require[i] <- : e var_j e, j * * Res-vars = map Vars Res * provide, require = * { [ j | vars-Re_j <- Res-vars, j /= i, var_i `in` vars-Re_j ] * , [ j | var_j <- Res-vars[i] `*` vars, i /= j] * | var_i <- vars * } * */ $func CAV e.vars (e.assigns) (e.delayed) = e.assigns; $func Get_Vars e = e; Get_Vars (e.Re) = (); Create_Aux_Vars (e.vars) e.splited_Re = ) (e.vars)> :: e.list, :: s.box, :: s.provide_i, :: s.require_i, { e.vars : e1 t.var_i e2, { e.list : e ((e.vars_Re) t.var_j) e, \{ t.var_i : t.var_j = >; e.vars_Re : e t.var_i e = ; }, $fail; e.splited_Re> :: t.Re_i, ) ())>, , ; }, $fail;; }, (/*assigns*/) (/*delayed*/)>; /* * Если есть переменная, у которой список provide пуст, её можно посчитать. * Это выражается в том, что она (вместе с присваиваемым значением) добавляется * в список assigns, убирается из списка vars, а также из всех списков provide * и delayed. В списках require её не было. * * CAV Res vars provide require assigns delayed = * { i | var_i <- vars, provide_i == [] } -> // Здесь неверно! На переменные * из delayed тоже надо смотреть. * vars = vars - var_i * provide = [ provide_j - i | provide_j <- provide ] * assigns = assigns++[(var_i, Res[i])] * delayed = [ (var_j, provide_j - i) | (var_j, provide_j) <- delayed ] * CAV Res vars provide require assigns delayed */ $func Assign_Empty_Provides e.vars = e.assigns (e.vars); Assign_Empty_Provides { e1 (t.var_i t.Re_i (/*empty provide_i*/) (e.require_i)) e2 = :: s.vars, { e1 e2 : e (t.var_j t.Re_j (e.provide_j) (e.require_j)) e, ) (e.require_j))>, $fail;; }, (t.var_i t.Re_i) >; e.vars = /*empty*/ (e.vars); }; /* * Если есть переменная, у которой список require пуст, кладём её в delayed. * Она будет посчитана, когда у неё опустеет список provide, т.е. когда не * останется переменных, у которых она в списке require. */ $func Delay_Empty_Requires e.vars = e.delayed (e.vars); Delay_Empty_Requires { e1 t.var e2, t.var : (t.var_i t.Re_i (e.provide_i) (/*empty require_i*/)) = :: e.delayed (e.vars), t.var e.delayed (e1 e.vars); e.vars = /*empty*/ (e.vars); }; /* * Выбор переменной (из двух) с более длинным списком требуемых ей значений. */ $func Max_Require e = e; Max_Require t.arg1 t.arg2 = t.arg1 : (t.var1 t.Re1 t.provide1 (e.require1)), t.arg2 : (t.var2 t.Re2 t.provide2 (e.require2)), { ) ()> = t.arg2; t.arg1; }; /* * Подставить вспомогательную переменную вместо исходной во всех результатных выражениях. * Присваивание к исходной переменной убрать (оно к этому моменту уже выполнено). * Убрать переменную из списков зависимостей. */ $func Subst_Aux_Var e = e; Subst_Aux_Var t.var t.aux (t.v t.Re (e.provide) (e.require)), { t.var : t.v = /*empty*/; ( t.v () () ); }; /* * Извлечь присваивание из всей информации о переменной. */ $func Extract_Assigns e = e; Extract_Assigns (t.var t.Re e) = (t.var t.Re); /* * Основной цикл обработки присваиваний. * * 1) Из всех переменных (в том числе и отложенных), от которых больше ничего * не зависит, сделать присваивания. * 2) Все переменные, которые больше ни от чего не зависят, отложить. * 3) Если осталось хотя бы две неотложенных переменных, выбирать из них ту, * которая зависит от наибольшего числа переменных, подставить везде вместо * неё вспомогательную, перейти к пункту 1. */ CAV e.vars (e.assigns) (e.delayed) = :: e.new_assigns (e.vars), e.assigns e.new_assigns :: e.assigns (e.delayed), e.delayed :: e.delayed (e.vars), { e.vars : t t e = : (t.var t.Re e), :: t.aux, e.assigns (t.aux (t.var)) (t.var t.Re) :: e.assigns, :: e.vars, :: e.delayed, ; e.assigns ; }; //***************** Компиляция сопоставления с образцом ******************* Get_Clash_Sequence (e.last_Re) t.Pattern e.Snt = (/*e.clashes*/) (RESULT e.last_Re) t.Pattern e.Snt $iter { e.Snt : (RESULT e.Re) (s.dir e.Pe) e.rest = /* * Компилируем все константные выражения и заводим в табличке все * незаведённые переменные. У старых переменных очищается память * на предмет клешей, в которых они раньше использовались. */ : (e.R1) (e.P1), )> : e, (e.clashes (e.R1) (s.dir e.P1)) e.rest; } :: (e.clashes) e.Snt, # \{ e.Snt : \{ (RESULT e.Re) (LEFT e) e = e.Re; (RESULT e.Re) (RIGHT e) e = e.Re; } :: e.Re, ; } = (e.clashes) e.Snt; IsWithout_Calls e.Re = e.Re $iter { e.Re : t.Rt e.rest = t.Rt : { (CALL e) = $fail; (BLOCK e) = $fail; (PAREN e.Re1) = ; t.symbol_or_var = /*empty*/; }, e.rest; } :: e.Re, e.Re : /*empty*/; $func CC s.Istail (v.fails) t.end_cycle e.Snt = e.asail_Snt; Comp_Clashes (e.clashes) s.Istail (v.fails) e.Sentence = , e.Sentence>; $func CC_Known_Lengths t.fail e.idxs = e.conds; $func CC_Compute_Length t.fail t.end_cycle t.idx = e; $func CC_Unknown_Lengths t.fail e.idxs = e.conds; $func CC_Deref t.fail e.actions = e.actions; $func CC_Eqs t.fail (e.assigns) e.eqs = e.actions; CC s.Istail (v.fails) t.end_cycle e.Snt, { : v.clashes = ; : (t.clash) e = t.end_cycle t.clash> ; : e.clashes = :: e.conds, /* * Когда мы добрались до сюда, все условия на длины на текущем уровне * выписаны. Невыполнение любого из оставшихся условий (на * соответствие типов, равенство, длины внутри скобок) ведёт не к * прекращению текущего цикла, а переход к его следующей итерации. * Поэтому в качестве t.end-cycle везде дальше подставляется текущий * откат. */ : { v.actions = e.conds v.actions> e.Snt>; /*empty*/ = e.conds () > :: e.actions, , { :: e.assign = e.actions () e.assign> e.Snt>; { :: s.split (e.left) (e.right) (e.len) t.var t.l_var t.r_var = { e.left : 0, e.right : 0 = /*empty*/ t.var; : t.sub_var, (DECL Expr t.sub_var) (ASSIGN t.sub_var (SUBEXPR t.var (e.left) ((INFIX "-" (e.len) (e.left e.right))))) t.sub_var; } :: e.subexpr t.var, { s.split : RSPLIT = t.r_var t.l_var "DEC-ITER"; t.l_var t.r_var "INC-ITER"; } :: t.l_var t.r_var s.iter_op, :: t.cont_label, :: t.break_label, e.actions e.subexpr (s.split t.var () t.l_var t.r_var) (FOR (t.cont_label) (t.break_label) () ((s.iter_op t.var)) (IF ("ITER-FAILS" t.var) >) e.Snt> (BREAK t.break_label) ); e.actions ; }; }; }; }; CC_Known_Lengths (e.fail) e.idxs, { e.idxs : (t.idx) e.rest = , : (e.len_Re) (e.len_Pe), ("IF-INT-CMP" "!=" (e.len_Re) (e.len_Pe) e.fail) ; ; }; CC_Compute_Length (e.fail) (e.end_cycle) t.idx = : t.var s.mult (e.minuend) (e.subtrahend), :: e.min, { t.var : ("Len-Var" e) = , ("IF-INT-CMP" "<" (e.minuend) ((INFIX "+" (e.subtrahend) ((INFIX "*" (e.min) (s.mult))) )) e.end_cycle ); :: t.m_var e.m_assign, :: t.s_var e.s_assign, ("IF-INT-CMP" "<" (t.m_var) ((INFIX "+" (t.s_var) ((INFIX "*" (e.min) (s.mult))) )) e.end_cycle ) :: e.min_cond, : { /*empty*/; e.max = ("IF-INT-CMP" ">" (t.m_var) ((INFIX "+" (t.s_var) ((INFIX "*" (e.max) (s.mult))) )) e.end_cycle); } :: e.max_cond, (INFIX "%" ((INFIX "-" (t.m_var) (t.s_var))) (s.mult)) :: e.div_cond, :: t.len_var e.len_assign, , , , :: e.clashes, >)> : e, e.m_assign e.s_assign e.min_cond e.max_cond ("IF-INT-CMP" "!=" (e.div_cond) (0) e.fail) e.len_assign; }; $func Get_Min e = e; $func? Get_Max e = e; CC_Unknown_Lengths (e.fail) e.idxs, { e.idxs : (t.idx) e.rest = : (e.len_Re) (e.len_Pe) (e.vars_Re) (e.vars_Pe), { :: e.max = :: e.min, ("IF-INT-CMP" "<" (e.len_Re e.max) (e.len_Pe e.min) e.fail); /*empty*/; } :: e.cond1, { :: e.max = :: e.min, ("IF-INT-CMP" ">" (e.len_Re e.min) (e.len_Pe e.max) e.fail); /*empty*/; } :: e.cond2, e.cond1 e.cond2 ; ; }; Get_Min { t.var e.vars = ; /*empty*/ = /*empty*/; }; Get_Max { t.var e.vars = : v.max, v.max ; /*empty*/ = /*empty*/; }; $func Pos (e.Re) s.dir e.pos = e.pos; Pos { (e.Re) RIGHT e.pos = (INFIX "-" ((LENGTH e.Re)) (e.pos)); (e.Re) LEFT e.pos = e.pos; }; /* * Информацию о проверках и заведении переменных, необходимых для создания * клешей из содержимого скобок, кодируем на ASAIL. */ CC_Deref (e.fail) e.actions, e.actions : { ("SYMBOL?" e.Re (s.dir e.pos)) e.rest = (IF ("SYMBOL?" e.Re ()) e.fail) ; (DEREF t.var e.Re (s.dir e.pos)) e.rest = (DECL Expr t.var) (ASSIGN t.var (DEREF e.Re ())) ; /*empty*/ = /*empty*/; }; CC_Eqs (e.fail) (e.assigns) e.eqs, { e.eqs : ((e.Re) (s.dir e.pos) t.Pt (e.len)) e.rest = { e.Re : t, : e.len (), // FIXME: здесь надо использовать // калькулятор s.dir e.pos : \{ LEFT 0; RIGHT e.len; } = e.Re;; } :: e.Re_term, { e.len : 1 = "TERM-EQ"; // FIXME: здесь надо использовать // калькулятор EQ; } :: s.eq, :: e.pos, { \{ : True = t.Pt (e.Re); t.Pt : \{ (REF e); (STATIC e); }, { = e.Re_term (t.Pt); t.Pt (e.Re); }; } :: el (er), (IF (NOT (s.eq el (er) (e.pos))) e.fail) :: t.cond, { /* * Мы предполагаем, что во всех пришедших e.eqs все e.Re * уже были определены ранее. */ e.assigns : $r e1 (s.op t.Pt e.def) e2 = ; t.cond ; }; , { t.Pt : (SVAR e) = (IF (NOT ("SYMBOL?" e.Re (e.pos))) e.fail );; } :: e.cond, { : s = e.cond ; : e, e.cond ; }; }; e.assigns e.eqs; }; Gener_Label e.QualifiedName = { : s.num, ; 1; } :: s.num, , (e.QualifiedName s.num); Add_To_Label (e.label) e.name = ; Lookup_Func t.Fname, \{ ; ; } : s.linkage s.tag t.pragma (e.Fin) (e.Fout) = s.linkage s.tag t.pragma (e.Fin) (e.Fout);