*! validly v3.2.1 KIMacdonald 13Aug2013 program validly, byable(onecall) rclass version 10 // see below local vers 3.2 local vdate "v3.2.1 13Aug2013" * * * (c) Kenneth Macdonald * Nuffield College, Oxford, OX1 1NF, UK * kenneth.macdonald@nuffield.ox.ac.uk * * PROGRAM VERIFICATION * The program is complex (and in places, having grown by accretion, * less elagant than it might be). But it has been extensively tested. * A test file: * validate_validly.do * accompanies this program. * * PROGRAM STRUCTURE * The only 'innovative' part of this coding is section F * which uses cond() to define logical (and implement relational) operators, * The rest is simply an interpreter-into-RPN and various degrees * of pretty-printing and chat. * * EFFICIENCY * Not much thought has been given to the CPU efficiency of the * interpreter-into-cond [CPU cost here negligible] * BUT attention has been paid to make the *generated coding* * as CPU-efficient as possible (taking advntage of indicator-variables; * avoiding recalculation; so forth); * further: since &| are commutative, and R can be made so, * in any particular we check whether * e.g. "s1 & s2" is "more efficient" than "s2 & s1" ? * in tersm of minimising cond calls (and within that, bracket depth) -- not sure if this * is the optimal strategy, but it does produce more compact code. * Intuition might suggest that coding specifically to 3-way relations * (so p&q&r,p|q&r,p&q|r etc etc) might lead to some efficiencies; * exploration suggests this is only very limitedly true, bearing in mind * that p,q,r may be non-indicator variables, and would make the parsing more complex. * That avenue is in abeyance, for the present. * * other things that might be done: * in extended: track no-emv vars (since we have that info) to simplify their coding; * in source: keep track of all-logically-poss-source-codes attavhing to s[i] to hardcode * "no-way s1-e,v could match s2-emv", again simplifying (we already do this when * s1 or s2 are vars) * (if one could readily find emvs for a var this could be applied to extended mode also) * * * VERSION of Stata * validly works happily on v10, hence version setting above * (to set that to later version would merely exclude those running 'old' * Stata versions without any advantage to others) * Admittedly the 'infinite string length' of V13 would have made some * programming simpler, but the code was mostly developed when v12 was latest, * so let it stand. * * * Note: no explicit *break* provision since this program is not relevantly sensitive * * * A. Decode the command, into various expressions * * For each expression: (reporting actions if chat) * B Split active string into characters/symbols, marking bracket level * C Mark logical/relational brackets * D whence, Compacting into logical/relational operators and args * E Parsing into RPN * F Compile RPN into nested functions cond(); choose appropriate one * * Then EXECUTE command as appropriate, and issue reminders etc * * * DEBUG NOTE: (since you are reading the code ...) There is a * 'debug(n)' option (not documented in the accompanying help file) * which sets: `chat2' ; levels are increasing-inclusive, range 0-9 * 0 off * 1 echoes final executed command (a useful minimal check) * 5 shows working of parser * 6 additional inelegant reporting * (undefined levels are free for use) * set_debug n sets the debug default; overridden by debug(n) * * * Flags: local actionlist "" local actionA "" local actionlistREF exp ifexp ifnot else when local actionAREF X C N E W local alphaLC "abcdefghijklmnopqrstuvwxyz" local arithopF 0 // arithmetic & extended (so that misbehaviour info can be given) local arithopC "" //the first-found operator local assert 0 //flag for assert local assertOPN "" //assert options passthru local asstring 0 //flag for assigned-string local by "" // by text local byF 0 //by flag local cedilla = char(184) local chat 0 //print switch local chat2 0 // the debug switch local chatnote "{c |}{col 18}{it}note{sf} {c +} " // to simplify local chatnotee "{c |}{col 18}{err}{it}note{sf}{txt} {c +} " // to simplify local chatremind "{c |}{col 14}{it}reminder{sf} {c + } " local col23A "{txt}{c |}{col 23}" local col23 "`col23A'{c |} " // single space after, so sometimes need another local compactF 1 // flag to compact by commutation local compactN 0 // counter for inversions local dagger = char(134) local default0 [if/] [in] [, Source(passthru) Sourcez Detail Extended debug(numlist int) Widely /// IFNot(passthru) else(passthru) when(passthru) local default `default0' NOPromote ] local defaultx `default0' ] local digits "0123456789" local dots = char(133) local else "" local elseALL 0 //flag for (else & absent ifnot), so else mops all local elseF 0 local elseRPN "" //printable RPN local elseCN 0 //number of compactings, this fn local emissC 0 // flag for `note extended e-m in fnC' local equals " =" //ease assert reporting local equivalentF1 0 // flag for equivalence reporting local equivalentF2 0 // flag for equivalence reporting local expCN 0 //number of compactings, this fn local expU "" // active form in final evaluation for assert local extended "" local extendedF 0 //flag for extended option local extdefF1 0 // this and next, report flags on default extended local extdefF2 0 local extdefF3 0 local fromvy 0 // flag for from-vy, no use at present local genF 0 // flag for generate local global 0 //flag for global - also set by wrap local globalIF 0 local g_short 0 // pretty print on g,ge for gen local ifexp "" // ifexp in any task local ifexpF 0 // flag for gen/rep if-clause local ifexpU "" // active form in final evaluations local ifnot "" // local ifnotF 0 //ifnot flag local ifnotRPN "" //printable RPN local ifnotCN 0 //number of compactings, this fn local impute 0 //imputed gen replace local isstring 0 // flag for string var local inF 0 // flag for in local intext "" local isNVAR 0 // counter for sourceNVAR local mark "{txt} {hline 6}{c TRC} " // indentation marker, for validly local mark2 "{txt}{space 8}{c |}{space 1}" local mark3 11 // corresponding _col()for followon local marku `"local mark \`mark2'"' // update mark after use local markn `"\`mark'{it}note{sf}: "' // just to save repetition local markD "" // fn layout local metmissF 0 // flag for missing optimisation local metN 0 //number of evermet nonmiss variables local mline 78 // max line length frem 'reg' local nonindF 0 // flag for GEN_TV on non-indicator vars local noneede 0 // flag for source+extended local nop "" // the nopromote carry-thru option local numer one two three four five local plainEVER 0 // flag for a 'plain' argument ever encountered local operators `"== >= <= != > < ! & | "' local possible "" //null option local precedence "" // for r(prcedence) local prevEMV 0 // flag for emvs in partial replace local qt "quietly " local range "" local refers 0 //flag for "conditional invokes result var local remindF 0 // reminder switch local remindeverF 0 // flag to simplify remind reporting for nonchat local repF 0 // flag for replace local repALL 0 // flag exhaustive replace local resultvar 0 //additional switch on resultvar only local returnRPN "" //simple RPN local rmist 0 //total literal relations involving mdc local smax 10 //set stacksize max local sourceCHAR 97 //active char() for e-mv in source local sourceREF 97 // reference point local sourceF 0 //flag for source (E|R) local sourceR 0 //flag for random source local sourceVAR "" //source vars local sourceNVAR "" //vars lacking mvs in source local sourceXM "" //associated extended-missing codes local sourcewarnF 0 //flag to warn on interpretation of 'p R emv' local sourcewarnEMV " " //the emv local sourceprev 0 // counter for previous used source codes local stringEMV ".s" local stringTRIP 0 // flag potential invocation of stringEMV local stringTRIP 0 // flag to help source table report local typelist byte int long float double // for generate local type "" // for generate explicit 'type' specification local usevars 0 //flag to use workspace vars in computing local validlyDefault il ed te ct ng nd ss t2 // use all, else updates may confuse local vlabND "" // marker for undefined value label local var "" //ease assert reporting local vert " " // replaced by vertical line {c |} for some reporting local vvers "{c RT}v`vers'{c LT}" local warn_max 0 // flag for max() reminder local warn_mi 0 //flag for mi() warning local warn_min 0 //flag for min() reminder local warn_mv 0 //flag for pRmissing warning local when "" local whenF 0 local whenRPN "" local whenTXT "" //the txt for when "& zvarW" local whenVAR "" //the actual var " & `zvarW'" local whenRAW "" //the when expression local whenCN 0 //number of compactings, this fn local whenCONS 0 //flag for when constant local widely "" //null otion local wrap 0 //flag for wrapped command local w0 0 //flag for explicit R local zvar "" local zvarC "" local zvarW "" // just nulls, in case of inadvertant print * * local rpn `"`r(RPN)'"' // lest needed for call to vy RPN below local precedence "`r(precedence)'" local tempifcond `"`r(ifcond)'"' * (otherwise it gets scrubbed before then (by captures I think)) return local vers `vers' * if _by() { local by "by `_byvars'`_byrc0': " local byF 1 } * NOTE when executing generate and replace commands below * 'by' has no effect where (as for some in `usevars' mode) * reference is only to existent variables;so it is there left silent * * has it been called from vy? (currently only affects OP layout) if strpos(`"`0'"',"`dots'") { // that untypable flag, added by vy local 0 = subinstr(`"`0'"',"`dots'"," ",.) // scrub it local mark "{txt} {c -}{c TRC} " // remember to use quoted form if using as arg local mark2 "{txt}{space 3}{c |}{space 1}" local mark3 6 local fromvy 1 // lest needed at some time } * * keep track of settings for session in global macro capture confirm ex $validlyDefault if _rc!=0 DEFAULT else { //paranoia kicks in local j 0 local k 0 foreach i of local validlyDefault { // not to be confused with $validlyDefault local j = (strpos("$validlyDefault","`i':")!=0)+`j' local ++k } if `j'!=`k' { // very implausible; but caught in alien use di "{col `mark3'}{err}Note:{txt} {help validly##macrotoggle:{bf}{c S|}validlyDefault{sf} already defined}" /// " (but not by current {bf}validly{sf})," while 1 { di "{col `mark3'}{space 6}{txt}can {bf}validly{sf} use it now (y/n)?" _r(validly_t_m_p_) if "$validly_t_m_p_"=="Y"|"$validly_t_m_p_"=="y" { DEFAULT macro drop validly_t_m_p_ continue, break } if "$validly_t_m_p_"=="N"|"$validly_t_m_p_"=="n" { di "{err}Continuation deemed " _c macro drop validly_t_m_p_ error 310 } } } * * get default debug setting DEFVAL t2 local chat2 `r(default)' // to allow early debug - see above } * *______________________________________________ * A. decode the command * * use "syntax" to decode where possible, since it does useful checking * * local 0 = subinstr(`"`0'"',"()","( )",.) // kludge for a() ## reinstate if reinstate assert * * to handle spaces within strings; put a marker there, unscramble later local imax = length(`"`0'"') local i 0 local quote 0 local zero "" while `++i'<= `imax' { //scan active exp local c = substr(`"`0'"',`i',1) if `"`c'"'==`"""' local quote = 1 - `quote' if `"`c'"'=="" local c " " if `quote' & `"`c'"'==" " local c `dots' // space marker local zero = `"`zero'"'+`"`c'"' } if `quote' { di as err "Missing closing quote, " _c error 198 } local 0 `"`zero'"' * OK, embedded string spaces now marked * local 0 = itrim(`"`0'"') // tidy unquoted spaces to singles local raw `"`0'"' // storing to allow typing as reminder * * note: ", else()" etc is parsed as if no "else(ex)" has been given; * but is better seen as an error (otherwise user may not notice absence=inaction): local 0 = subinstr(`"`0'"',"( )","()",.) // collapse "else( )" etc to ease detection foreach x in else() when() ifn() ifnot() source() s() { if strpos(`"`0'"',"`x'") CANT `x' "no argument" } * if "`1'"=="egen" NOEGEN "`mark'" "`mark2'" //issue apology - note quotes since mark contains spaces else if "`1'"=="global" { * *trim first two params out of 0, reducing 0 gettoken action 0 : 0, parse(" =") local action global gettoken var 0 : 0, parse(" =") confirm names `var' gettoken ift : 0, parse(" =") if "`ift'"=="if" { capture syntax if/ [, Detail debug(numlist int) Widely ] * to give user reminder, via TELL_G, of restricted syntax, if they have made error TELL_G "`mark'" "`mark2'" `mark3' `mline' // only writes if error syntax if/ [, Detail debug(numlist int) Widely ] local globalIF 1 local ifexp = trim(`"`if'"') local if "" // clear the if-condition, which otherwise confuses later reporting ## } else { //no if local 0 = "="+`"`0'"' // add = to allow syntax analyser to work capture syntax =/exp [, Detail debug(numlist int) Extended ] * to give user reminder, via TELL_G, of syntax if they have made error TELL_G "`mark'" "`mark2'" `mark3' `mline' // only writes if error syntax =/exp [, Detail debug(numlist int) Extended ] } local global 1 if `byF' CANT by global } else { // not global if "`1'"=="generate" | "`1'"=="gen" |"`1'"=="replace" | "`1'"=="rep" | "`1'"=="g" /// | "`1'"=="ge" | "`1'"=="gene"| "`1'"=="gener" | "`1'"=="genera" |"`1'"=="generat" { * (I would prefer to restrict to gen/generate, but standard Stata syntax allows all) if length("`1'")<3 { // at least mutter about these, reduce confusion on implicit di as txt "`mark'{inp}`1'{txt} is read as '{ul:g}enerate' (as is Stata's convention)" `marku' local g_short 1 } * * NOTE: =/exp in *syntax* returns _rc=109 type-mismatch ERROR when * it encounters a string expression; [seems a Stata "feature"] * so we need following kludge to handle: capture syntax anything =/exp // checking just the exp bit if _rc==109 { // that error might come from valid string expression: ="cat" local zanything `anything' //on this error, anything is apparently returned OK local j=strpos(`"`0'"',"=")+1 if `j'==1 error 198 local 0 = substr(`"`0'"',`j',.) local zeroshift `"`0'"' //store this bit syntax anything `default' local 0 = `"=(`anything')=="text""' //make a string-equivalence expression capture syntax =/exp //see whether anything=="text" parses if _rc==0 { //valid string local asstring 1 local 0 `"`zeroshift'"' //since that last call has overwritten if and in etc syntax anything `default' local exp `"`anything'"' } else error 109 // the initial error 109 is real local 0 `"`zero'"' //restore } // non 109 errors will anyway fail at the next step * if `asstring' local anything `zanything' // recover else syntax anything =/exp `default' * tokenize `anything', parse(": ") // to allow :value-label * if "`1'"=="replace" |"`1'"=="rep" { if `"`3'"'!="" { di as err `"'`1' `2' `3'' not allowed"' error 101 } local var `2' VCHECK `asstring' `var' local action replace local repF 1 } else { local action generate local genF 1 if `chat2'>=5 di as err `"1|`1'|, 2|`2'|, 3|`3'|, 4|`4'|, 5|`5'|, 6|`6'|"' * now extract type and value-label if appropriate, checking if `"`2'"'=="" error 111 else if `"`3'"'=="" local var `2' else if `"`4'"'=="" { TLABEL 1 `2' `3' // this call ignores `2' local type `2' local var `3' } else if `"`5'"'=="" { TLABEL 0 `3' `4' local vlabND `r(tlabel)' local var `2' local vlab `4' } else if `"`6'"'=="" { TLABEL 0 `4' `5' local vlabND `r(tlabel)' local type `2' local var `3' local vlab `5' } else error 198 * if "`vlab'"!="" local vlab ":`vlab'" //for calls * if "`type'"!="" { //check that 'type' is correct local j 1 foreach i of local typelist { if "`i'"==`"`type'"' { if `asstring' CANT `type' "string expression" local j 0 continue, break } } if `j' { // not numeric local i = substr(`"`type'"',4,.) if indexnot(`"`i'"',"`digits'")==0 & length("`type'")<=6 { local n = real("`i'") if substr("`type'",1,3)=="str" & `n'>0 & `n'<=244 { if !`asstring' CANT `type' "numeric expression" local j 0 } } } if `j' { di _n `"'{bf}`type'{sf}' not a {help data_types:recognised data type}"' error 109 } local type " `type'" //prettyprint } confirm new variable `var', ex } if `chat2'>=5 di as err `" report assembled gen/rep, exp1:`exp' if: `if' in: `in' "' } else if "`1'"=="assert" { * *trim first param out of 0, reducing 0 gettoken action 0 : 0, parse(" =") local 0 = `"=`0'"' syntax =/exp [if/] [in] [, Detail debug(numlist int) Possible Widely Rc0 Null Fast] local ifexp = trim(`"`if'"') local assert 1 local equals "" //stet if "`rc0'"!="" | "`null'"!="" | "`fast'"!="" local assertOPN ", `rc0' `null' `fast'" } else if "`1'"=="gen_test_vars" { //logically detached utility to make test data gettoken action 0 : 0, parse(" =") //trim out generate_test_vars syntax newvarlist(max=10) [, Extended Nonind Ind ] * that 'ind' just to let me smuggle null setting in test-file if "`extended'"!="" local extendedF 1 if "`nonind'"!="" local nonindF 1 GEN_TV "`varlist'" `extendedF' `nonindF' exit } else if "`1'"=="" | "`1'"=="`dots'"{ // to handle both 'validly' amd 'vy' * prettyprint for returned r(RPN) local n = wordcount(`"`rpn'"') // that was seved earlier local flag 0 if !`n' { di as txt "{col `mark3'}{res}r(RPN){txt} not currently available" // ### exit } di as txt "{space 6}Displaying the input and the resulting {help validly##RPN:RPN} coding, " if "`precedence'"!="" di "{space 6}with {help validly##precedence:precedence reminders} where issued, " di "{space 6}from the last call to {bf}validly{sf}; as found in {help validly##saved:r(RPN)}" local nn 0 //loop across components local ne 0 // expression count forvalues i = 1/`n' { local j = word(`"`rpn'"',`i') local ++nn if `nn'==1 { // expression name local j = substr("`j'",2,.) local jk = subinstr("`j'",":", "",.) local k = 4 - length("`jk'") local j = (2*`ne')+2 if "`precedence'"!="" local flag = word("`precedence'",`j') // loaded same order as rpn local ++ne } if `nn'==2 { // expression foreach op of local operators { // insert spaces for legibility if "`op'"=="<" | "`op'"==">" | "`op'"=="!" continue // skip lest get > = local j = subinstr(`"`j'"',"`op'", " `op' ",.) } local kj = length(`"`j'"') + 2 di as txt _n "{space 6}{c TLC}{hline `kj'}{c TRC}" di as txt " `jk' {hline `k'}{c RT}" _c di `" {inp}`j'{txt} {c LT}"' } if `nn'==4 { // rpn form * local jj `"{txt}{c RT} "' di as txt " as{space 2}" _c if `flag' di as txt "{c LT}{c -}precedence invoked" else di "{c |}" di `" RPN {c -}{txt}{c RT} "' _c local foot `"{txt}{c BLC}{c -}"' local j = subinstr(`"`j'"',"`cedilla'", " `cedilla' ",.) // so that word works local j = subinstr(`"`j'"',`"""', "`dagger'",.) // so that word works on strings * note that printing has to be piecemeal because the {inp}{txt} will probably overflow strings * ## should perhaps anyway checke here fpor stringlength also foreach v of local j { // identify component for layout local kk 0 if `"`v'"'=="`cedilla'" { local kk 1 // seperator } else { foreach op of local operators { if `"`v'"'=="`op'" { local kk 2 // operator continue, break } } } if `kk'==1 { * local jj `"`jj'`v'"' di " {c |} " _c local foot "`foot'{c -}{c BT}{c -}" } else { if `kk'==2 di `"{inp}`v'{txt}"' _c // operator else { // expressioon local v = subinstr(`"`v'"',"`dagger'", `"""',.) // reset quotes di `"`jj'{res}`v'{txt}"' _c } local ii = length(`"`v'"') local foot "`foot'{hline `ii'}" } } di " {c LT}" di as txt "{space 6}`foot'{c -}{c BRC}" } if `nn'==5 local nn 0 // reset component loop } return local RPN `"`rpn'"' // this so returns are not destroyed on unargument vy if "`precedence'"!="" return local precedence "`precedence'" if `"`tempifcond'"'!="" return local ifcond `"`tempifcond'"' exit } else if substr("`1'",1,4)=="set_" { return local RPN `"`rpn'"' // this so returns are not destroyed on set if "`precedence'"!="" return local precedence "`precedence'" if `"`tempifcond'"'!="" return local ifcond `"`tempifcond'"' * if "`1'"=="set_impute" { TOGSET te `2' exit } else if "`1'"=="set_extended" { TOGSET ed `2' if "`2'"=="ON" |"`2'"=="on" { di as txt "`mark'OK, extended {ul:on}{space 3}but {it}note{sf} that having {bf}extended{sf} mode as the default" _n /// "`mark2'{space 18}is {help validly##extended1:computationally expensive}" ULINE `mline' `mark3' } exit } else if "`1'"=="set_detail" { TOGSET il `2' exit } else if "`1'"=="set_compact" { TOGSET ct `2' exit } else if "`1'"=="set_nullstring" { TOGSET ng `2' exit } else if "`1'"=="set_reminder" { TOGSET nd `2' exit } else if "`1'"=="set_nomiss" { TOGSET ss `2' exit } else if "`1'"=="set_debug" { // undocumented option, see above TOGSET t2 `2' // global marker "chat2:", preventing user guess at option exit } else if "`1'"=="set_defaults" | "`1'"=="set_default" { // the non-"s" option just being kind DEFAULT di as txt "`mark'all switches reset to {help validly##minor:default settings}" ULINE `mline' `mark3' exit } else { di as err "{col `mark3'}`1' is, for {bf}validly{sf}, an " _c error 199 // tough if attempt at wrapping a set_ command } } else { //implied gen/replace OR wrapper * assume it is implied, unless it fails to parse as such: * * seek implied (but without `type', 'vlabel', 'nopromote'): capture syntax name(name=var) =/exp `defaultx' if _rc==0 { local impute 1 // implied local ifexp = trim(`"`if'"') } else if _rc==109 { // this error - se above - might come from valid string expression: eg ="cat" local zero `"`0'"' //store gettoken var 0 : 0, parse(" =") gettoken temp 0 : 0, parse(" =") //trimming only 2 local zeroshift `"`0'"' //store this bit ## capture syntax anything `defaultx' if _rc==0 & "`temp'"=="=" { //OK so far; check further that is string local 0 = `"=(`anything')=="text""' //make a string-equivalence capture syntax =/exp //see whether anything=="text" parses if _rc==0 { //valid string local impute 1 local asstring 1 local 0 `"`zeroshift'"' //since that last call has overwritten if and in etc syntax anything `defaultx' local exp `"`anything'"' local ifexp = trim( `"`if'"') } } local 0 `"`zero'"' //restore } * if `impute' { // is impute, what type? capture confirm variable `var', ex if _rc==0 { //extant, so impute replace, check permission and type DEFVAL te if !`r(default)' { di as txt _n " You have {help validly##rutility:{bf}validly set_impute{sf} configured to {bf}OFF{sf}}" /// _n " so, since {bf}`var'{sf} exists, present command {err}fails, as " _c error 110 } VCHECK `asstring' `var' //check type local action replace local repF 1 } else { //new, impute gen, hope valid confirm new variable `var', ex local action generate local genF 1 } } * if !`impute' { // assume wrapped command * * first find validly’s own options local temps = strpos(reverse(`"`0'"'),",,") if `temps'!=0 { // validlys options local zero `"`0'"' local 0 = substr(`"`0'"', -`temps', `temps'+2) syntax [, Detail debug(numlist int) Widely] local 0 = subinstr(`"`zero'"',","+`"`0'"',"",1) //temp delete v's opts } local zero `"`0'"' local ci 0 // flag for comma local q 0 // withinquote flag local n = length(`"`0'"') forvalues i=1/`n' { local c = substr(`"`0'"',`i',1) if `"`c'"'==`"""' local q = 1 - `q' if `"`c'"'==`","' & !`q' { // unquoted comma local ci `i' continue, break //out of forvalues } } if `ci'!=0 { // are single-comma (=command's) options if !`temps' { // if no doubles, remark di as txt "`mark'attaching {help validly##wrap:single-comma} option(s)" /// `" to {help validly##errorW:perceived wrapped} {bf}`1'{sf}"' `marku' } local 0 = substr(`"`0'"',1,`ci'-1) } * * and parse, looking for 'if' and no =exp capture syntax [anything] [fweight aweight pweight iweight] [if/] [in] if _rc!=0 { di as err _n "Check for misspellings, misspecified variables, inappropriate" _n /// "or duplicate options, or other varieties of " _c error 198 } if `"`if'"'=="" { di `"`mark'{txt}Without an {ul:if} clause, we simply execute: {inp}.`by'`zero'{txt} "' ULINE `mline' `mark3' `by'`zero' exit } else { // wrapper local ifexp = trim(`"`if'"') local action global local global 1 * local var valid_if_exp_ // ## local wrap 1 local if "" //## } } } } * * OK, expression read; * ______________________________________________________________________________ * macro drop _tempifcond // since can be long * but first task is to get chat sorted DEFVAL il if `r(default)' local detail = "detail" if "`detail'"!="" local chat 1 if `chat' & `g_short' ULINE `mline' `mark3' //just aesthetics * * explicit debug status overides default setting if "`debug'"!="" local chat2 `debug' * if `chat2'>=5 di as text "{hline}" _n " `vvers' `vdate'" _n "{hline}" * di control: chat=0 results, chat=1 +RPN & cond, * chat=3 anything flagged - additional reassurance plus RPN compiler workings * caht=2 (set by setting the default as debug AND invoking debug) * minimal reportage of actual gen/replace/wrap/global commands issued * This is quite a good checking mode (though it messes layout) * local exp = trim(`"`exp'"') * local rpn "" local precedence "" // I think have by now been reset, but just-in-case * * local exp = trim(`"`exp'"') //since seems to come with trailing blank * * next, handle IF * local step 1 // default for both global and plain * local active `"`exp'"' //exp1 active whether (1) global, plain, or (4)if *************************************************************** * if "`vlabND'"!="" & !`chat' { //chat OP appears later di as txt "`markn'{help validly##extype:value label} '{inp}`vlabND'{txt}'" /// " has yet to be defined" `marku' } * if `"`if'"'!="" { local ifexpF 1 // a gen/rep if-clause exists local ifexp = trim(`"`if'"') // already done for global/wrap/assert if appropriate local ifTXT " if" } else local ifTXT "" //for reporting * * set widely local wide = "`widely'"!="" if `wide' & !(`ifexpF'|`wrap'|`globalIF') NEEDS widely if * * set extended from option only now (to smplify source report below) if "`extended'"!="" local extendedF 1 // from option * if `chat' { local markD "{col 7}{txt}{c |} " //layout in exp report local qt "" } * * set compactor iff permitted DEFVAL ct if !`r(default)' local compactF 0 * * set reminder for syntax inversions DEFVAL nd if `r(default)' local remindF 1 * * set switch for attending to mvs in coding DEFVAL ss if `r(default)' local metmissF 1 * * set extended missing value for null-string relations DEFVAL ng local stringEMV `r(default)' * * attend to source and source(.x,r) if "`source'"!="" { //bracketed local sourceF 1 local sourcerr 0 local sourceraw `source' // for error report local source = subinstr("`source'","source(","",1) local source = subinstr("`source'",")","",1) local source = subinstr("`source'",","," ",1) local i = word("`source'", 1) local j = word("`source'", 2) if "`i'"=="r" { // for source(r) local sourceR 1 if "`j'"!="" local sourcerr 1 } else { // assuming source(.x,r) or source(.x) if length("`i'")>2 local sourcerr 1 if "`j'"=="r" local sourceR 1 else if "`j'"!="" local sourcerr 1 local k = substr("`i'", 1,1) if "`k'"!="." local sourcerr 1 local k = substr("`i'", 2,1) local jj = (`sourceCHAR'+26) //alphabet plus one forvalues m = `sourceCHAR' / `jj' { local mm `m' if "`k'"==char(`m') continue, break } if `mm'==123 local sourcerr 1 local sourceCHAR `mm' local sourceREF `mm' } if `sourcerr' { di as txt _n "error in parameters in {inp}`sourceraw'" error 198 } } else if "`sourcez'"!="" local sourceF 1 if `sourceR' local sourceF 1 * if `impute' & !`chat' { // appearing before notes di as txt "`mark'interpreted as a request to " _c if `repF' di "{err}`action'{inp} `var'{txt}" //`it' else di "{inp}`action' `var'{txt}" //`it' `marku' } * if `sourceF' { if `extendedF' { local noneede 1 // flag for later chat report if !`chat' { di as txt "`markn'{help validly##sourcecode:no need to specify {bf}{ul:e}xtended{sf}} " /// "in addition to {bf}{ul:s}ource{sf}" `marku' } } local extended "extended" //source implies extended mode local extendedF 1 } * * set extended from switch DEFVAL ed if `r(default)' { if `assert' | `globalIF' | `wrap' { // only switch on where pertinent if !`chat' di as txt "`markn'suppressing, for this command, current default {bf}extended:on{sf} setting" `marku' local extdefF1 1 } else { if `extendedF' & !`sourceF' { // dont mutter when implied by source if !`chat' di as txt "`markn'{bf}extended{sf} is already set as default" `marku' local extdefF2 1 } else local extendedF 1 } } * * remaining checks if "`nopromote'"!="" { if `genF' CANT nopromote generate // only an option in explicit gen/replace local nop ", nop" // for calls } if `asstring' { if `sourceF' CANT source "string-variable {bf}`var'{sf}" if `extendedF' { if "`extended'"=="" { // tolerant if is default setting only local extendedF 0 if !`chat' di as txt "`markn'suppressing, for string variable, the default '{bf}extended:on{sf}' setting" local extdefF3 1 `marku' } else CANT extended "string-variable {bf}`var'{sf}" } } if `byF' { // given sequential calls, we must not let the by variables be modified foreach x of local _byvars { if "`x'"=="`var'" CANT "modifying `var'" "its use in {bf}by{sf}" } } * local possibleF = "`possible'"!="" * if !`chat' { //minimal need-to-know if `global' { local i "" if `extendedF' local i " extended" di as txt "`mark'valid`i' version of {c RT}{inp} " _c `marku' if `globalIF'|`wrap' di `"if `ifexp'{txt} {c LT}"' _c else di `"`exp'{txt} {c LT}"' _c local i = `mark3'+14 if length(`"`exp'"')+`mark3'>=20 di _n _col(`i') _c di `" placed in {res}"' _c if `wrap' di `"{c 'g}ifcond'{txt}, {help validly##savecond:{bf}r(ifcond){sf}}"' else di `"{c S|}`var'{txt}"' if `globalIF' & !`wide' TELL_NV `"`mark'"' //OK in wide mode, since retains raw if !`wrap' & !`globalIF' TELL_NC "`var'" `"`exp'"' `"`mark'"' } if `wide' & !`assert' { di "`mark'selecting observations when condition is true {it}{ul:or indeterminate}{sf}" `marku' } if `assert' { if `ifexpF' { di `"`mark'Considering observations where expression {inp}`ifexp' {txt}"' _n /// "`mark2'(when validly interpreted) is " _c if !`wide' di "{ul:true};" else di "true {it}{ul:or indeterminate}{sf};" di "`mark2'amongst these, " _c } else di as txt "`mark'" _c `marku' if `possibleF' di "{ul:only} observations where {c RT} {inp}`exp' {txt}{c LT} (validly interpreted)" _n /// "`mark'is {ul:known false}" _c else di `"any observations where {c RT} {inp}`exp' {txt}{c LT} (validly interpreted)"' _n /// "`mark'is false {ul:or unknown}" _c di " will be seen as {ul:contradicting} the assertion." ULINE `mline' `mark3' } } * * * option ifnot if `"`ifnot'"' != "" { local ifnotF 1 if !`ifexpF' NEEDS ifnot if local i = length(`"`ifnot'"')-1 local ifnot = subinstr(substr(`"`ifnot'"',1,`i'),"ifnot(","",1) //extracting exp CHECKEXP `"`ifnot'"' `asstring' ifnot } //end of ifnot * * * option else if `"`else'"' != "" { local elseF 1 if !`ifnotF' local elseALL 1 // flag for 0&mi if !`ifexpF' NEEDS else if if `wide' CANT else wide local i = length(`"`else'"')-1 local else = subinstr(substr(`"`else'"',1,`i'),"else(","",1) //extracting exp CHECKEXP `"`else'"' `asstring' else } //end of else() * option when if `"`when'"' != "" { local whenRAW `", `when' is True"' local whenF 1 * if !(`ifnotF'|`elseF') NEEDS when ifnot|else local i = length(`"`when'"')-1 local when = subinstr(substr(`"`when'"',1,`i'),"when(","",1) //extracting exp CHECKEXP `"`when'"' 0 when // has to be numeric if !(`ifnotF'|`elseF') { local i = trim(`"`ifexp'"') local j = trim(`"`when'"') if `ifexpF' & !`wide' & !`extendedF' { // e works with replace, but less confusing to be silent if !`chat' { di `"`markn'exactly equivalent to {inp}.vy `var' = `exp' if (`i') & (`j'){txt}"' `marku' } local equivalentF1 1 } if !`ifexpF' & !`extendedF' { // gen in e mode fills-in from if, unlike when() if !`chat' { di `"`markn'exactly equivalent to {inp}.vy `var' = `exp' if `j'{txt}"' `marku' } local equivalentF2 1 } } } //end of when() * * * make reporting/action strings for ‘in’ if "`in'"!="" { local in " `in'" // to save layout local intext " in {it}range{sf}" local range = subinstr("`in'","in","",1) local inF 1 } * * * Decide on computation mode: * usevars 1 which utilises workspace variables * usevars 0 for simplest '=p' or '=p if q' (default) if `ifnotF' | `elseF' | `whenF' local usevars 1 * if a 'fresh generate we followthru conditional emvs if !`wide' & `genF' & !`elseF' & `extendedF' & `ifexpF' local emissC 1 if `emissC' local usevars 1 // that also needs worksapce vars * * output header: if `chat' { local i = `mline'-10 di as txt _n "{c TLC}{hline `i'}`vvers'{hline 2}{c TRC}" if `wrap' di `"{txt}{c |} Generating a version of {c RT}{inp} if `ifexp' {txt}{c LT}"' else { di "{txt}{c |} A version of {input}" _c if `impute' & `repF' di "{err}" _c di "`action'{txt}" if `global' { di "{txt}{c |} to construct a macro " _c if `globalIF' di "for use in conditions" _n "{txt}{c |} (so considering {inp}if exp{txt} )" else di } } di "{text}{c |} {ul:correctly} handling 'missing' in logical/relational expressions." local it "" if `ifnotF'|`elseF'|`whenF' { local it = ", " if `ifnotF' local it = "`it'" + "ifnot({it}exp{sf}N) " if `elseF' local it = "`it'"+ "else({it}exp{sf}E) " if `whenF' local it = "`it'"+ "when({it}exp{sf}W)" } di as txt "{c |}" _n "{txt}{c BLC}{hline 14}{c TRC}" _n "{space 3}{txt}considering {c +}{c -} " _c if !`global' di "{inp} `var'`equals' {it}exp{sf}" _c if `ifexpF'|`wrap' | `globalIF' di " {inp}if {it}exp{sf}C" _c else if `global' di "{inp} exp" _c if !`wrap' di "`intext'`it'{txt}" _c di _n "{txt} {c TLC}{hline 13}{c BRC}" _n " {c |} " _c if !`global' & (`ifexpF'|`usevars') di "assembling functions, as fitted to their deployment in the evaluation below" else di "and assembling the appropriate function:" di " {c BLC}{hline 76}" } * * assemble an action list matching this call local k 0 foreach ii of local actionlistREF { // possible exps local ++k if `"``ii''"'!= "" { local `ii' = trim(`"``ii''"') // tidying trailing blamks in paasing local actionlist `actionlist' `ii' local jj = word("`actionAREF'",`k') local actionA `actionA' `jj' } } * * next fill the decision array dij, for processing RPN below: * symbols: start#=1 |=2 &=3 R=4 !=5 (=6 )=7 end#=8 * dij= 1 is rc 4 is ## 9 is err local d "111119413111133133111331333113313332133211111291333393339999999955521555" local m 0 * * Decision table: * #start=1, #end=8 function=@ * 2 3 4 5 6 7 8 9 * | & R ! ( ) # @ * +---------------- * 1| 1 1 1 1 1 9 4 1 * 2| 3 1 1 1 1 3 3 1 * 3| 3 3 1 1 1 3 3 1 * 4| 3 3 3 1 1 3 3 1 * 5| 3 3 3 2 1 3 3 2 * 6| 1 1 1 1 1 2 9 1 * 7| 3 3 3 3 9 3 3 3 * 8| 9 9 9 9 9 9 9 9 * 9| 5 5 5 2 1 5 5 5 * if `chat2'>=5 di _n "Decision table:" _n "#start=1, #end=8 function=@" _n /// " 2 3 4 5 6 7 8 9" _n " | & R ! ( ) # @" _n " {c TLC}{hline 16}" _c forvalues i=1/9 { // put d into 'array' dij if `chat2'>=5 di _n " `i'{c |}" _c forvalues j=2/9 { local ++m local d`i'`j' = substr("`d'",`m',1) if `chat2'>=5 di " `d`i'`j''" _c } } if `chat2'>=5 di _n * * set reminder counters for invocations of precedence rule; local remindEXP 0 // number of expressions affected * for chat the following are zeroed after each expression * for !chat they cumulate across all expressions local remind1 0 // flag to count where &| precedence upsets LR sequence local remind2 0 // dito relation vs logical local remind2a "" // string of relations local remind2b "" // corresponding logicals * reset at each pass: local ticktock 0 // handle double references local remindTHIS 0 // expression counter * * * OK, now examine each expression, decoding as appropriate * ______________________________________________________________________________________________ * _________________________________________________________________________________________________________ * _____________________________________________________________________________________________ * local actionn 0 //action counter, only used for W lookup below foreach what of local actionlist { * _________________________________________________________________________________________________ * local active `"``what''"' // active is processed local ++actionn local W = word("`actionA'", `actionn') //capital what if "`W'"=="X" local W "" // X to allow being fed as arg * * for simplification, set flags for "this expression is': foreach i of local actionlistREF { // clear all local is`i' 0 } local is`what' 1 // this exp * local everN 0 // flag for any no-mv simplifications this command local everIND 0 // flag for any indicator simplification actually used in this command local sourceD 0 // flag to register whether potential source var in fact had mv * behaviour of source and extended is expression-specific; * expW never heeds; * expW heeds only on generate, without else or wide local emiss 0 // expression-specific flag for 'use e-m' local esourceF 0 // ditto for source local esourceR 0 // ditto for sourceR if `isexp' | (`isifexp' & `emissC') | `isifnot' | `iselse' { local emiss `extendedF' local esourceF `sourceF' local esourceR `sourceR' } * * validly assumes the user leaves all the "handling of misssing-tests" to it * but we should check that user has followed this sdvice: * this is test for warn_mi; warn_mv is set later * We also check min(), max() to give reminder foreach x in mi min max { local temp `"`active'"' local i = strpos(`"`temp'"',"missing(") if `isifexp' & `i'>0 local warn_mi 1 while `warn_`x''==0 { // mi( screened against other fn ending in mi( local i = strpos(`"`temp'"',"`x'(") if `i'==0 continue,break local c = substr(`"`temp'"',`i'-1,1) local j 1 if `"`c'"'!="" local j= indexnot( `"`c'"', "`alphaLC'"+upper("`alphaLC'")+"`digits'"+"_") if `j'==1 { if "`x'"=="mi" { if `isifexp' local warn_`x' 1 } else local warn_`x' 1 continue, break } local temp = substr(`"`temp'"',`i'+2,.) } } // checking complete * __________________________________ * B. Split active string into characters/symbols, marking bracket level * local funf 0 //flag to note function call inthis exp local n 0 //decomposed string length local p 0 //bracket level local imax = length(`"`active'"') local i 0 if `chat2'>=5 di as err `" reporting active exp## `active' ##"' local outstring 1 //to denature symbols within strings local subscript 0 // and symbols in subscripts while `++i'<= `imax' { //scan active exp local c = substr(`"`active'"',`i',1) if `"`c'"' ==`"""' local outstring = 1 - `outstring' else if `"`c'"' ==`"["' local ++subscript if `"`c'"' ==`"]"' local --subscript if `"`c'"' !="" { //only consider nonspace if `"`c'"' =="~" local c "!" //simplify to one negation symbol if `outstring' & !indexnot(`"`c'"',"<>=!") { //symbols if substr(`"`active'"',`i'+1,1)=="=" { // composites local x`++n' = `"`c'"'+"=" local ++i } else { if "`c'"=="=" ERR197 `"`raw'"' 1 local x`++n' = `"`c'"' } local t`n' 4 // relation if `"`x`n''"'=="!" local t`n' 5 // negation local q`n' `p' } else { local x`++n' `"`c'"' local t`n' 0 local q`n' `p' if `outstring' { if `"`c'"'=="(" { local ++p local q`n' `p' } else if `"`c'"'==")" { local --p if `p'<0 ERR197 `"`raw'"' 2 } else if `"`c'"'=="|" local t`n' 2 else if `"`c'"'=="&" local t`n' 3 } } if `outstring' & !`subscript' & `emiss' & !`arithopF' & !indexnot(`"`c'"',"+*/-^") { local arithopF 1 //side-task, look for arithmetic ops local arithopC "`c'" } } } if `p'!=0 | `n'==0 | `subscript' ERR197 `"`raw'"' 3 * (probably this - and calls to ERR197 above - unnecessary since picked up by 'syntax') * if `chat2'>=5 { //chatter local i=0 di as inp " x[i] t[i] q[i] before markup" while (`++i'<= `n') { //1 di as res `" `x`i'' `t`i'' `q`i`'' "' } } * so x is 'vector' of characters, length n * their type in t * and their bracket level in q * __________________________________ * C Mark logical/relational brackets * local i=0 while (`++i'<= `n') { //1 if (`t`i''>=2&`t`i''<=5) & `q`i''>0 { //binary or logical * doing it even for Unary, which will be redundant but not bad * works without need to check intervening operators because of way levels works local p `q`i'' // level local j `i' while (`--j'>0) { if `"`x`j''"'=="(" & `q`j''==`p' { local t`j' 6 continue, break } } local j `i' while (`++j'<=`n') { if `"`x`j''"'==")" & `q`j''==`p' { local t`j' 7 continue, break } } } if `t`i''==5 { //unary not -- further test for following bracket local j=`i'+1 if `"`x`j''"'=="(" { local p `q`j'' local t`j' 6 while (`++j'<=`n') { if `"`x`j''"'==")" & `q`j''==`p' { local t`j' 7 continue, break } } } } } * (probably this ## - and calls to ERR197 above - unnecessary since picked up by 'syntax') * if `chat2'>=5 { // chatter local i=0 di " {res} x[i] t[i] q[i] aftr markup" while (`++i'<= `n') { //1 if `t`i''>0 di "{inp}" _c if `t`i''==6 |`t`i''==7 di "{txt}" _c if `q`i''==1 local h "#" if `q`i''==2 local h "##" if `q`i''==3 local h "###" if `q`i''==4 local h "####" di `" `x`i'' `t`i'' `q`i'' `h' {res}"' } } * __________________________________ * D whence, Compacting into logical/relational operators and args * local k 0 local i 0 * top : local x0 "#" local t0 1 // start symbol while (`++i'<= `n') { if `t`i''==6 & `t`k''==0 { // bracket, nonlogic before * Presently, an 'error': * we get here if * EITHER a function contains logical/relational expression * OR logical/relational expression pre-operated on by arithmetic. * The second restriction (unless validly were to decode ALL operators to RPN and reconstitute them) * is probably unavoidable (there is also a further call to MIXED below for arithmetic POST-operators). * There may in future be ways around the first restriction? local function `x`k'' * extract, for report, title of specific function local i= indexnot( reverse(`"`function'"'),"`alphaLC'"+upper("`alphaLC'")+"`digits'"+"_") if `i'!=0 local function = substr(`"`function'"',length(`"`function'"') - `i' + 2,.) local j 0 if `i'==1 { local j 1 // pre-operator flag local k `active' } else local k `function' MIXED `j' `vvers' `k' // report, exit with error } local ++k if (`t`i''!=0) { // operator local t`k' `t`i'' local x`k' `"`x`i''"' //note: compacting back into x } else { local t`k' 0 local x`k' `"`x`i''"' local j `i' while `++j'<=`n' { if `t`j'' !=0 { local --j continue, break } local ct `"`x`j''"' if `"`ct'"'=="`dots'" local ct " " //restoring quoted space local x`k' = `"`x`k''"' + `"`ct'"' } if `"`x`k''"'=="(" | `"`x`k''"'==")" local --k * above, since zero level singleton brackets are * redundant logical or relational brackets local i `j' } } * and tail: local ++k local x`k' "#" local t`k' 8 // end symbol, distinguished by type local plain =`k'==2 // flag for expression without logical/relational operators *______chatter: if `chat2'>=5 { local c "" di _n "{txt}Packed version of exp{res}`c'{txt}:" di as txt " Cell{c |}typ{c |}char " local i= -1 while (`++i'<= `k') { if `i'<10 di `" `i' {c |} {res}`t`i''{txt} {c |} {res}`x`i''{txt}"' else di `" `i' {c |} {res}`t`i''{txt} {c |} {res}`x`i''{txt}"' } } *^^^^^^end chatter. * * __________________________________ * E Parsing into RPN * * From compacted x into r, index ir * using as sidings * symbol stack s, index is * variable stack b, index ib * * these vectors contain not values, but pointers to x, t * NOTE x,t run from 0 * and negative pointer in ib points to ir * forvalues i = 1 / `smax' { local s`i' 0 local b`i' 0 } local rmax 200 // the result can grow either end - but cant use neg names local rmin 200 // so centre r on 200, arbitrarily for prettyprint local ib 0 local i 0 local m 0 // now scan from first read item in x1 local is 1 // prime the symbol stack with start local s1 0 // start symbol in x0,t0 local s2 0 // to allow first while * __________________________________ * while `t`s2''!=8 | `ib'>0 { //until endsymbol but while args * __________________________________ * we repeatedly deal with the symbol stack, * only getting fresh chars when symbol stack flags no-action * *______chatter: if `chat2'>=5 { local ++m di _n "{inp}Step `m' {txt}" _n " {c |} Sym{c |} Operands (cell, content)" local a = max(`is',`ib')+1 while `--a' >0 { local w1 `"`x`s`a'''"' local w4 = length(`"`w1'"') if `w4'<2 local w1 `" `w1'"' if `a'>`is' local w1 " " if `a'>`ib' di `" {c |} {res}`w1'{txt} {c |} "' else{ if `b`a'' > 9 di `" {c |} {res}`w1'{txt} {c |} `b`a'' {res}`x`b`a'''{txt}"' else if `b`a'' >= 0 di `" {c |} {res}`w1'{txt} {c |} `b`a'' {res}`x`b`a'''{txt}"' else di `" {c |} {res}`w1'{txt} {c |} `b`a'' {inp}Stack{txt}"' } } di " {c BLC}{hline 4}{c BT}{hline 10} " if `rmin'<`rmax' { //print stack, and marker for top expression di "Stack:" local it = `rmax' +1 local ikk 1 //operands excess while (`--it' > `rmin') { if `t`r`it'''>0 & `t`r`it'''!=5 local ++ikk //dyadic ops di `" `it' {c |} {inp}`x`r`it'''{txt} `t`r`it''' "' if `t`r`it'''==0 { //operand local --ikk if `ikk'==0 { di " {hline 7}" local ikk 9999 // only marking top exp } } } } else{ di "Stack: {inp}empty{txt}" } } *^^^^^^end chatter. if `is'== 1 local dd 1 // no action on first entry else { // real comparisons local is2 =`is' - 1 local dd `d`t`s`is2'''`t`s`is'''' //decision lookup * if `chat2'>=5 di `"Relate sym {res}`x`s`is2'''{txt} to {res}`x`s`is'''{txt} {hline 2} decision {res}`dd'{txt}"' if `chat2' & `dd'==3 & (`t`s`is'''==4) & (`t`s`is2'''==4) /// di as err "`mark'{err}`x`s`is2''' and `x`s`is''' have merely left->right precedence order{txt}" * if `remindF' { //check, if enabled, for syntax sequence inversions if `is'>2 & `dd'==1 { if `chat2'>=5 di as err `"PRECEDENCE `t`s`is''' ## `t`s`is2''' ## `x`s`is''' ## `x`s`is2''' "' if (`t`s`is'''+`t`s`is2''')==5 { local ticktock = 1 - `ticktock' // handling double-call each occurrence if `ticktock' { local remindTHIS 1 // flag for rule-invoked--this-exp local ++remind1 // & | } } if (`t`s`is'''==4) & (`t`s`is2'''==2 | `t`s`is2'''==3) { // relation v logical local remind2 1 local ticktock = 1 - `ticktock' // as above if `ticktock' { local remindTHIS 1 // flag for rule-this-exp local remind2a `"`remind2a' `x`s`is'''"' // cumulate local remind2b `"`remind2b' `x`s`is2'''"' } } } } } if `dd'==5 { // move fn -- ## currently inactive ERR197 `"`raw'"' 51 // ### local funf 1 local ib3 = `ib' -2 local r`rmin' `b`ib3'' // pretext local --rmin local ++rmax local r`rmax' `b`ib'' //posttext local --ib local --ib local ++rmax local r`rmax' `s`is2'' //@ marker local s`is2' `s`is'' local --is local b`ib' = 0 -`rmax' //E } else if `dd'==3 { // construct result stack * treat monadic (ie !) separately if `t`s`is2'''==5 | `t`s`is2'''==9{ //negation or fn if `b`ib''>0 { //plain local ++rmax local r`rmax' `b`ib'' } * E requires no action since in stack } else { //dyadic local ib2 = `ib' -1 * negative pointers are E markers * depending upon position vars are added to head or tail if `b`ib2''>0 & `b`ib''>0 { //plain local ++rmax local r`rmax' `b`ib2'' local ++rmax local r`rmax' `b`ib'' } else if `b`ib''>0 & `b`ib2''< 0 { //just add ib local ++rmax local r`rmax' `b`ib'' } else if `b`ib''<0 & `b`ib2''> 0 { //add ib2 at head ##of top exp * ## positioning operand just-after top exp in stack local itkk= `rmin'+1 if `rmin'<`rmax' { //do I here need this ## local it = `rmax' +1 local ikk 1 while (`--it' > `rmin') { //find length, as in chatter about stack above if `t`r`it'''>0 & `t`r`it'''!=5 local ++ikk //dyadic ops if `t`r`it'''==0 { //operand local --ikk if `ikk'==0 { local ikk 9999 // only marking top exp local itkk `it' } } } } local --itkk if `chat2'>=5 di as err "REPORT consider `itkk' rather than `rmin' " //## if `itkk'!=`rmin' { if `chat2'>=5 di as err " REPORT moving" local jtkk=`itkk'-1 forvalues iikk=`rmin'/`jtkk' { local jjkk=`iikk'+1 local r`iikk' `r`jjkk'' } local r`itkk' `b`ib2'' } else local r`rmin' `b`ib2'' local --rmin } * NOTE E E requires no action, since already in stack local --ib //one fewer param } * *operator: local ++rmax local r`rmax' `s`is2'' local s`is2' `s`is'' local --is * *reference: local b`ib' = 0 -`rmax' } else if `dd'==2 { //scrap brackets local --is local --is } else if `dd'==4 { //startend if `ib'== 1 { //deal with possible single param if `b`ib''>0 { local ++rmax local r`rmax' `"`b`ib''"' } //no action needed on stack pointer } else if `ib'>1 MIXED 1 "`vvers'" "`active'" * This condition IS an error, * and (I think) is triggered uniquely by arithmetic * POST-operating on logical/relational expression continue, break } else if `dd'==1 { // no symbol action, so getnext thing local ++i //no need to boundcheck ? since t8 traps if `t`i''==0 { //char into char stack local ++ib local b`ib' `i' } else { //symbol into symbol stack local ++is local s`is' `i' } } else ERR197 `"`raw'"' 5 * __________________________________ } // while * __________________________________ * * assembling RPN record even for plain expressions local nrpn 1 // which section - using nrpn rather than n since needed later local rpnPL 7 // print line length, to presrve pretty print * can't do it on length of assembled string since it carries unprinting smcl local rpns "" // also plain RPN for macro store * {c RT] if placed in rpns will di OK, but remains as {c RT] within rpns, so local it `rmin' //since rmin points below end while (`++it'<= `rmax') { //avoid evaluation =s, cause with syntax is longstring local arg `"`x`r`it'''"' local argn = length(`"`arg'"') local rpnPL = `rpnPL' + `argn' + 3 if `it'==(`rmin'+1) { local rpn`nrpn' `"{txt}{c RT}"' local rpnB`nrpn' "{col 7}{txt}{c LT}" } else { local rpn`nrpn' `"`rpn`nrpn'' {txt}{c |}"' local rpnB`nrpn' `"`rpnB`nrpn''{c -}{c BT}"' local rpns `"`rpns'`cedilla'"' if (`rpnPL' >= (`mline'+2)) & (`it'!=`rmax') { // allow last to overflow a bit local ++nrpn local rpn`nrpn' `"{txt}{c RT}"' local rpnB`nrpn' "{col 7}{txt}{c LT}" local rpnPL 7 } } local rpns `"`rpns'`arg'"' if `t`r`it'''==0 local rpn`nrpn' `"`rpn`nrpn''{res} `arg'"' else local rpn`nrpn' `"`rpn`nrpn''{inp}{bf} `arg'{sf}"' local k = length(`" `arg'"') local rpnB`nrpn' `"`rpnB`nrpn''{hline `k'}"' } local rpn`nrpn' `"`rpn`nrpn''{txt} {c LT}{c -}"' local rpnB`nrpn' `"`rpnB`nrpn''{c -}{c BRC}"' local activet = trim(`"`active'"') local activet = subinstr(`"`activet'"'," ","",.) // `active' has embedded space=dots local rpns = subinstr(`"`rpns'"'," ","`dots'",.) // set rpns to match * visually clearer; added benefit easier parsing for vy RPN local returnRPN `returnRPN' [exp`W': `activet' »» `rpns' ««] * * * keep track of precedence invocation for r(precedence) if `remindF' { local i 0 if `remindTHIS' { local i 1 local ++remindEXP } local precedence "`precedence' exp`W' `i' " } * * __________________________________ * * F Compile RPN into nested functions cond(), into s1 * __________________________________ * * Evaluate RPN * (re)using S[is] as the working computational stack * (Note: this section began life as a stand-alone routine; * it could be rewritten simply to use pointers to r[] * but calculation astack is conceptually clearer, so leave as is.) * to build up the nested cond() function * through the use of nested macros * * s[i] is stack * cs[i] flag whether indictor (with appropriate mvs) var or exp * ns[i] no missing values (var or exp) * ss[i] ‘sourced’ variable; if so the emv * vs[i] if ss[i], the variable * forvalues i = 1 / `smax' { // set start stack values local s`i' "null" // emptying stack local cs`i' 0 //flag whether stack entry is now indicator local ns`i' 0 //flag whether stack entry is variable with no missintg-values local ss`i' 0 //flag whether stack entry is a 'sourced' variable (if so, the emv) local vs`i' 0 // if ss[i], it gives the implicated variable } * comment: * cs[i] set if cell is an indicator variable (with apt mmvs), or an invoked cond(...) * knowing cell is well-behaved allows some simplifications * ns[i] allows e.g. !p to be coded merely as !p (Stata operators OK if no mv) * (could encode cs[]and ns[] into one, but ensuing tests simpler like this) * ss[i] allows eg !p to be expressed as cond(`vs1',0,1,`ss1') i.e. cond(p,0,1,.a) as against the * standard extended formula cond(`s1',0.1.`s1') which expamds to * cond(cond(p,p,0,.a),0.1.cond(p,p,0,.a)) * local i 0 // stack counter local it `rmin' //since rmin points below end local rmis 0 //for pretty print literal mdc local compactN 0 //reset counter for optimising local s1_mF "ERROR" // missing->False form of s1 (only) * the mF form of s1, if coded directly, will be more compact than having * to add a step coding cond(s1,1,0,0) * (note mF always reduced to 1,0,0 - no interest in actual value) * only used in functional (i.e. !`usevars') evaluation; * simplest code is "garner all" and "use last" * while (`++it'<= `rmax') { local y `"`x`r`it'''"' local m `t`r`it''' if `m'==5 { // negation if `i'<1 error 102 * if `ns1' { // no-missing in s1 local s1_mF !`s1' // if s1 is p&q that will already be bracketed local s1 !`s1' local everN 1 // flag for simplification report } else if `ss1' { // can simplify for source local s1_mF cond(`vs1',0,1) //pre-mod s1 local s1 cond(`vs1',0,1,`ss1') //ss1 entails emiss } else { local s1_mF cond(`s1',0,1) //pre-mod s1 -- missing returns if `emiss' local s1 cond(`s1',0,1,`s1') else local s1 cond(`s1',0,1,.) } local cs1 1 // flag as indicator local ss1 0 // flag as no longer simple source * note ns1 retains its value } else if `m'>0 { // any dyadic operator local vacillate 1 // reset by nonmiss actions to avoid swaps below * * two COMPACT calls here just for report when chat>1 if `chat2'>=5 { COMPACT `"`s1'"' `chat2' s1 COMPACT `"`s2'"' `chat2' s2 } * results of more complex calls to COMPACT below are (on current * definitions (v2.5.1) obvioulsy computable from results on s1, s2; * but have left as is, partly laziness, partly facilitate trial of alternate * definitions within COMPACT * local setns1 0 // default, result contains missing * local s1t `"`s1'"' //to feed s1_mF local sysmiss . // other than source(r) if `esourceR' local sysmiss cond(int(2*runiform()),`s2' ,`s1') * if `m'==9 { //function etc ## inoperative ERR197 `"`raw'"' 91 if `i--'<3 error 102 if `chat2'>=5 di " {txt}Mixed portion becomes: {res}`s3'({inp}`s2'{res})`s1'{txt} " di as err " Mixed portion becomes: {res}`s3'({inp}`s2'{res})`s1'{txt} " local s1 `"`s3'(`s2')`s1'"' forvalues j = 2/`i' { // extra pop stack local k = `j'+1 local s`j' `"`s`k''"' } local --i } * if `m'==3 { // conjunction if `i--'<2 error 102 * if `ns1' & `ns2' { // both args nonmiss local s1 (`s2'&`s1') local s1_mF `s1' // after computed s1 OK local setns1 1 // so that propagates local vacillate 0 // to skip the comparison section local everN 1 // flag for simplification report ########## } else if `ns1'|`ns2' { // one arg nonmiss -- NOTE source handled also local p1 1 if `ns2' local p1 2 local p2 = 3 - `p1' * if `cs`p2'' { // indicator works as-as local a `s`p2'' local ++everIND // flag for actual invocation } else if `emiss' { if `ss`p2'' local a cond(`vs`p2'',1,0,`ss`p2'') // source simplification else local a cond(`s`p2'',1,0,`s`p2'') } else local a cond(`s`p2'',1,0,.) local s1_mF cond(`s`p1'',cond(`s`p2'',1,0,0),0) // must precede plain s1 assign local s1 cond(`s`p1'',`a',0) local vacillate 0 // to skip the comparison section local everN 1 // flag for simplification report ########## } else if `ss1'|`ss2' { // at least one source-var forvalues ii=1/2 { // store appropriate if `ss`ii'' { // source-var local SS`ii' `ss`ii'' // emv local VS`ii' `vs`ii'' // var } else { // not source-var, so use as-is local SS`ii' `"`s`ii''"' local VS`ii' `"`s`ii''"' } } if `ss2' & !`ss1' { // if only one source-var, it's neatest in S1 foreach ii in S V { local x `"``ii'S1'"' local `ii'S1 `"``ii'S2'"' local `ii'S2 `"`x'"' } } // swapped so source-var is 1 if `esourceR' local sysmiss cond(int(2*runiform()),`SS2' ,`SS1') if `ss1'&`ss2' { // we can hard-code the decision on both-sourced-missing since we know emvs if `"`VS1'"'==`"`VS2'"' local bothmiss `"`SS1'"' // substantively unlikely * of course could have simplified unlikely p&p to p; above just to avoid an error else local bothmiss `"`sysmiss'"' } else { // we have to test local bothmiss cond(`SS1'==`SS2',`SS1',`sysmiss') // SS1 is the known one } local s1_mF cond(`VS2',cond(`VS1',1,0,0),0,0) local s1 cond(`VS2',cond(`VS1',1,0,`SS1'),0,cond(`VS1',`SS2',0,`bothmiss')) local vacillate 0 // to skip the comparison section } else { // handling operands both potentailly containing missing * local z1 1 //start with s1 nd s2, then flip local z2 2 forvalues zi = 1/2 { //explore commutation local sxvalue `s`z1'' //option if known indicator vars local ++everIND // flag for actual invocation if `emiss' { // to track, as much as may be, "extended missing" if !`cs`z1'' { local sxvalue cond(`s`z1'',1,0,`s`z1'') //not indicator, so force 1,0, emv local --everIND } local s`z1'z cond(`s`z2'',`sxvalue',0,cond(`s`z1'',`s`z2'',0,cond(`s2'==`s1',`s2',`sysmiss'))) local s`z1'bz cond(`s`z2'',`sxvalue',0,cond(`s`z1'',`s`z2'',0,cond(`s2'==`s1',`s1',`sysmiss'))) * these differ only in which used to return missing if missing identical } else { if !`cs`z1'' { local sxvalue cond(`s`z1'',1,0,.) //not indicator, so force 1,0, sysmiss local --everIND } local s`z1'z cond(`s`z2'',`sxvalue',0,cond(`s`z1'',.,0)) * Note: elided fourth element in final cond() is intentional } local s`z1'_mFz cond(`s`z2'',cond(`s`z1'',1,0,0),0,0) //missing_>False form, same for emiss or not * capture cummutative scores: COMPACT `"`s`z1'z'"' `chat2' z`z1' local n`z1' = r(conds) local d`z1' = r(depth) if `emiss' { COMPACT `"`s`z1'bz'"' `chat2' "z`z1'b" local n`z1'b = r(conds) local d`z1'b = r(depth) } * local zt `z1' //commute local z1 `z2' local z2 `zt' } //end of explore commutation } // end of variaties of operands } else if `m'==2 { // disjunction -- comments as for conjunction if `i--'<2 error 102 * if `ns1' & `ns2' { // both args nonmiss local s1 (`s2'|`s1') local s1_mF `s1' // after computed s1 OK local setns1 1 // so that propagates local vacillate 0 // to skip the comparison section local everN 1 // flag for simplification report ########## } else if `ns1'|`ns2' { // one arg nonmiss -- NOTE source handled also local p1 1 if `ns2' local p1 2 local p2 = 3 - `p1' * if `cs`p2'' { local a `s`p2'' local ++everIND // flag for actual invocation } else if `emiss' { if `ss`p2'' local a cond(`vs`p2'',1,0,`ss`p2'') // source simplification else local a cond(`s`p2'',1,0,`s`p2'') } else local a cond(`s`p2'',1,0,.) local s1_mF cond(`s`p1'',1,cond(`s`p2'',1,0,0)) // must precede plain s1 assign local s1 cond(`s`p1'',1,`a') local vacillate 0 // to skip the comparison section local everN 1 // flag for simplification report } else if `ss1'|`ss2' { // at least one source-var forvalues ii=1/2 { // store appropriate if `ss`ii'' { // source-var local SS`ii' `ss`ii'' // emv local VS`ii' `vs`ii'' // var } else { // not source-var, so use as-is local SS`ii' `"`s`ii''"' local VS`ii' `"`s`ii''"' } } if `ss2' & !`ss1' { // if only one source-var, it's neatest in S1 foreach ii in S V { local x `"``ii'S1'"' local `ii'S1 `"``ii'S2'"' local `ii'S2 `"`x'"' } } // swapped so source-var is 1 if `esourceR' local sysmiss cond(int(2*runiform()),`SS2' ,`SS1') if `ss1'&`ss2' { // we can hard-code the decision on both-sourced-missing since we know emvs if `"`VS1'"'==`"`VS2'"' local bothmiss `"`SS1'"' // substantively unlikely * of course could have simplified unlikely p&p to p; above just to avoid an error else local bothmiss `"`sysmiss'"' } else { // we have to test local bothmiss cond(`SS1'==`SS2',`SS1',`sysmiss') // SS1 is the known one } local s1_mF cond(`VS2',1,cond(`VS1',1,0,0),cond(`VS1',1,0,0)) local s1 cond(`VS2',1,cond(`VS1',1,0,`SS1'),cond(`VS1',1,`SS2',`bothmiss')) local vacillate 0 // to skip the comparison section } else { // handling operands both potentailly containing missing * local z1 1 //start with s1 nd s2, then flip local z2 2 forvalues zi = 1/2 { //explore commutation local sxvalue `s`z1'' local ++everIND // flag for actual invocation if `emiss' { if !`cs`z'1' { local sxvalue cond(`s`z1'',1,0,`s`z1'') // force 1,0,emv local --everIND } local s`z1'z cond(`s`z2'',1,`sxvalue',cond(`s`z1'',1,`s`z2'',cond(`s2'==`s1',`s2',`sysmiss'))) local s`z1'bz cond(`s`z2'',1,`sxvalue',cond(`s`z1'',1,`s`z2'',cond(`s2'==`s1',`s1',`sysmiss'))) } else { if !`cs`z1'' { local sxvalue cond(`s`z1'',1,0,.) //not indicator, so force 1,0, sysmiss local --everIND } local s`z1'z cond(`s`z2'',1,`sxvalue',cond(`s`z1'',1,.,.)) } local s`z1'_mFz cond(`s`z2'',1,cond(`s`z1'',1,0,0),cond(`s`z1'',1,0,0)) * capture scores COMPACT `"`s`z1'z'"' `chat2' z`z1' local n`z1' = r(conds) local d`z1' = r(depth) if `emiss' { COMPACT `"`s`z1'bz'"' `chat2' "z`z1'b" local n`z1'b = r(conds) local d`z1'b = r(depth) } * local zt `z1' local z1 `z2' local z2 `zt' } //end of explore commutation } // end of operands with missing } * else if `m'==4 { // relation if `i--'<2 error 102 * if `ns1' & `ns2' { // both args nonmiss local s1 `"(`s2'`y'`s1')"' local s1_mF `"`s1'"' local setns1 1 // so that propagates local vacillate 0 // to skip the comparison section local everN 1 // flag for simplification report } else { // one or other has potentially missing local w0 0 * first check for relations to explicit missing-values or NULL forvalues j = 1/2 { // scan for mdc local w1 = substr(`"`s`j''"',1,1) local w2 = substr(`"`s`j''"',2,1) local w3 = length(`"`s`j''"') if (`"`w1'"'=="." & `w3'<=2 & indexnot(`"`w2'"',"`alphaLC'")==0) { if `isifexp' local warn_mv 1 local w0 =3 - `j' //the nonexplicit one local w5 `"`s`j''"' //for report if "`w5'"=="." local w5 sysmiss continue, break } * scan for null string if (`"`w1'"'==`"""' & `w3'==2 & `"`w2'"'==`"""') { if `isifexp' local warn_mv 1 local w0 1 local w5 NULL //for report continue, break } } if `w0' { // explicit missing-data reference * leaving this section with literal-source usage for simplicity local j 0 // report flag local ++rmis local rmist `rmis' //aggregate flag if "`w5'"!="sysmiss" & "`w5'"!="NULL" & "`y'"=="==" { //handle p==.x slightly differently local s1_mF `"(`s2'`y'`s1')"' local s1 cond(`s`w0''==.,.,`s2'`y'`s1') local j 1 // report flag } else { * this and above is only context in which realation can ends abuting relation unbracketed * expexp>r becomes p==.>r, so return bracketed: local s1 `"(`s2'`y'`s1')"' local s1_mF `"`s1'"' } * store instances for later report local rmisA`rmis' `"`y'"' local rmisB`rmis' `w5' local rmisC`rmis' `j' if "`w5'"!="sysmiss" & "`w5'"!="NULL" & `esourceF' { //worry about 'v R .x' in sourcemode local sourcewarnF 1 local sourcewarnEMV `w5' } } else { // code the varying s2 R s1 relationxx * first handle the one-nonmiss scenario * (note that one-nonmiss entails numeric) if `ns1'|`ns2' { // one or other nonmiss; source handled local jj 2 if `ns2' local jj 1 local S2 `s`jj'' // operand with missing local SS2 `s`jj'' if `ss`jj'' { // that one operand a sourced var local S2 `vs`jj'' local SS2 `ss`jj'' if `jj'==1 local s1 `S2' else local s2 `S2' } local s1_mF cond(`S2'<.,`s2'`y'`s1',0) // has to be before s1 * coding non-transparent; but s2Rs1 is assessed correct order if `emiss' local s1 cond(`S2'<.,`s2'`y'`s1',`SS2') else local s1 cond(`S2'<.,`s2'`y'`s1',.) local vacillate 0 // the coding as is local everN 1 // flag for simplification report } // end of one-nonmiss else if `ss1'|`ss2' { // at least one source-var -- so both numeric forvalues ii=1/2 { // store appropriate if `ss`ii'' { // source-var local SS`ii' `ss`ii'' // emv local VS`ii' `vs`ii'' // var } else { // not source-var, so use as-is local SS`ii' `"`s`ii''"' local VS`ii' `"`s`ii''"' } } if `ss2' & !`ss1' { // if only one source-var, it's neatest in S1 foreach ii in S V { local x `"``ii'S1'"' local `ii'S1 `"``ii'S2'"' local `ii'S2 `"`x'"' } } // swapped so source-var is 1 if `esourceR' local sysmiss cond(int(2*runiform()),`SS2' ,`SS1') if `ss1'&`ss2' { // we can further simplify if `"`VS1'"'==`"`VS2'"' local final `"`SS1'"' // substantively unlikely * of course could have simplified unlikely pRp above just to avoid an error else local final cond(`VS1'<.,`SS2',`sysmiss') } else { // we have to test local final cond((`SS1'==`SS2')|`VS1'<.,`SS1',`sysmiss') // SS1 is the known one } local s1_mF cond(`VS2'<.,cond(`VS1'<.,`s2'`y'`s1',0),0) local s1 cond(`VS2'<.,cond(`VS1'<.,`VS2'`y'`VS1',`SS1'),`final') local vacillate 0 // to skip the comparison section } else { // two nonmiss (or string) * First, identify string operands, since missing-test has to be coded differently * (function mi() would test either, but, in relations-of-relations, * we get nested calls, which Stata disallows; and in extended mode * we would anyway have to test for strings for result return if indeterminate) local isstring 0 local ink `in' // etc, since call to syntax will scrub local ifk `"`if'"' local expk `"`exp'"' foreach xit in s1 s2 { //## revised to handle spaces in stringso local xit `"``xit''"' * so instead deploy a test that is more general local 0 = "="+`"(`xit')"'+`"=="c""' //make a string-equivalence if `chat2'>=5 di as err `" Report STRING CHECK testing `0' "' capture syntax =/exp //see whether cell=="c" parses if _rc==0 { if `chat2'>=5 di as err " REPORT that is a string" local isstring 1 continue, break //no need to check both arguments } } // end foreach local in `ink' // restore local if `"`ifk'"' local exp `"`expk'"' * if `emiss' { * to track, as much as may be, "extended missing codes" if `isstring' { local s1 cond(`s2'!=""&`s1'!="",`s2'`y'`s1',`stringEMV') local stringTRIP 1 // so that source report can list } else { //the only assymetric-sized possible s1/s2 case local s1z cond(`s2'<.,cond(`s1'<.,`s2'`y'`s1',`s1'),cond(`s2'==`s1'|`s1'<.,`s2',`sysmiss')) COMPACT `"`s1z'"' `chat2' "z1(R)" local n1 = r(conds) local d1 = r(depth) local s2z cond(`s1'<.,cond(`s2'<.,`s2'`y'`s1',`s2'),cond(`s2'==`s1'|`s2'<.,`s1',`sysmiss')) * commuting s2 R s1 itself within 2nd cond would be immaterial to complexity COMPACT `"`s2z'"' `chat2' "z2(R)" local n2 = r(conds) local d2 = r(depth) } * Note: avoidance of mi() use is deliberate, to avoid later nesting. } else { //not emiss if `isstring' local s1 cond(`s2'!=""&`s1'!="",`s2'`y'`s1',.) else local s1 cond(`s2'<.&`s1'<.,`s2'`y'`s1',.) * note dont need brackets around y cause at this point s2,s1 well defined } if `isstring' local s1_mF cond(`s2'!=""&`s1t'!="",`s2'`y'`s1t',0) else local s1_mF cond(`s2'<.&`s1t'<.,`s2'`y'`s1t',0) //symmetrical, so no swap } } } // end of one or other missing ## } * * for conjunction, disjunction, emiss-relation now consider alternate pXq sequeces: if `vacillate' & (`m'==2 | `m'==3 |(`m'==4 & !`w0' & `emiss' & !`isstring')) { local s1 `s1z' if `m'!=4 local s1_mF `s1_mFz' //note: relation, s1_mF symmetrical if `compactF' { //consider switching only if enabled if `emiss' & `m'!=4 { local zt = min(`n1',`n1b',`n2', `n2b') // lowest cond count local list "1 1b 2 2b" } else { local zt = min(`n1',`n2') // lowest cond count local list "1 2" } local dm 500 foreach z of local list { //find smallest depth of lowest cond if `n`z''==`zt' local dm = min(`dm',`d`z'') } foreach z of local list { if `n`z''==`zt' & `d`z''==`dm' { // cond dominates, given above local s1 `s`z'z' if `m'!=4 local s1_mF `s`z'_mFz' //matching mF for conjunction,disjunction if "`z'"!="1" { // report if switched local ++compactN if `chat2'>=5 { if "`z'"=="1b" di "# Swapping final argument" else di "# Commuting to score `zt'" } } continue, break } } } } //decided * OK: forvalues j = 2/`i' { // pop stack local k = `j'+1 local s`j' `"`s`k''"' local cs`j' "`cs`k''" local ns`j' "`ns`k''" local ss`j' "`ss`k''" local vs`j' "`vs`k''" } local cs1 1 // flag type for evaluated exp local ss1 0 // not a source var local ns1 `setns1' // depends on what done, default is 0 } else{ // arguments if `i++'>=`smax' error 103 * forvalues j = `i'(-1)2 { //pushdown the stack local k = `j'-1 local s`j' `"`s`k''"' local cs`j' "`cs`k''" local ns`j' "`ns`k''" local ss`j' "`ss`k''" local vs`j' "`vs`k''" } local cs1 0 //default values local ns1 0 //default values local ss1 0 //default values local vs1 0 //default values * local s1 `"`y'"' // store arg local s1_mF cond(`s1',1,0,0) // mF disattends to emv * if `isifexp' { //now just reportage of refers #### modify to handle any when capture confirm variable `s1', ex if _rc==0 { // is a variable if "`var'"=="`s1'" local resultvar 1 if `resultvar' & (`ifnotF' | `elseF') local refers 1 } } * * if a numeric variable, * - check whether indicator var,and record * - if appropriate emv, can simplify subsequent cond calls * - with any emv, if `plain', affects how used * - if `source', insert appropriate emv local cs1 0 //default assumption: not binary local onezero 0 // cs[n] looks at mv match, this just 1,0 match capture confirm numeric variable `s1', ex * if _rc==0 { // arg is a numeric variable, so: * has it been met (only matters for numeric vars, since only for them do we do misstable/assert * to simplify exps, but themselves are expensive) local met 0 forvalues v = 1/`metN' { if "`s1'"=="`metV`v''" { // characteristics stored = ones ascertained local met `v' local emvX `metVemv`v'' local mvX `metVmv`v'' local assX `metVass`v'' continue, break } } if !`met' { // ascertain characteristics local ++metN capture misstable sum `s1' `in' local emvX =r(N_gt_dot) if "`emvX'"=="." local emvX 0 local mvX =r(N_eq_dot) if "`mvX'"=="." local mvX 0 local assX 0 capture assert (`s1'==0|`s1'==1|`s1'>=.) `in', f //indicator? if _rc==0 local assX 1 local metV`metN' `s1' local metVemv`metN' `emvX' local metVmv`metN' `mvX' local metVass`metN' `assX' } local ns1 0 // ## if `metmissF' local ns1 = !(`emvX'+`mvX') // to determine whether nomiss is attended to local cs1 0 if `assX' { // ie 0,1,any mv local onezero 1 //indicator, any mv pattern if `emiss' | `isifexp' | `iswhen' local cs1 1 // extended ok if in extended mode, or within conditions (which only attend in e else if !`emvX' local cs1 1 //binary with sysmiss ok in non-ext mode * here allowing more relaxed emv would not give errors, but would give puzzlingly * erratic passthru of emv to result * if `cs1' local everIND 1 -- we track actual use insteead #### } // end indicator test * if `esourceF' { // if source, set notional e-mvs by variable * this code predates the metV faclity to track characteristics of all numeric vars * it could be rewritten to use its lists, but source works as is, so left for now local isv : list posof "`s1'" in sourceVAR local isnv : list posof "`s1'" in sourceNVAR local sourceD 0 // default no missing if `isv'==0 & `isnv'==0 { //unmet var for source * capture assert(`s1'<.) `in', f // does it contain missing? * if _rc==0 { // no missing if `ns1' { // no missing local sourceNVAR `sourceNVAR' `s1' // keep track, save recalc local ++isNVAR } else { // new, missing values local jsv = "."+char(`sourceCHAR') if "`stringEMV'"=="`jsv'" { //avoid conflicts with string-missing local ++sourceCHAR local jsv = "."+char(`sourceCHAR') } if `sourceCHAR'>122 { // unlikely unless base high -- but e-mv exhausted if `sourceREF'>97 { local i = char(`sourceREF') di as txt _n "{ul:suggestion}: for option {bf}source{sf}," /// " {help validly##sourcev:use an alphabetically 'earlier' base;}" /// _n(2) "{err}given use of {bf}.`i'{sf} as base, " _c } else di as err _n "for option {bf}source{sf}, " _c error 103 } local sourceVAR `sourceVAR' `s1' local sourceXM = "`sourceXM'" + " `jsv'" local ++sourceCHAR local sourceD 1 // flag actuala sourced } } else if `isv'!=0 { // previously met local jsv : word `isv' of `sourceXM' local sourceD 1 // flag actual sourced } * * assign if ppropriate if `chat2'>=5 di as err " so we deploy `jsv' for `s1' " * for esource, the si_mF coding below becomes needlessly complex [it should be cond(`s1',1,0,0) * before next step] but immaterial because never used if `sourceD' { // var has mvs local ss1 `jsv' // flag for is-sourced-var local vs1 `s1' // sourced-var (note that s1 will effectively contain cond(`vs1',`vs1',0,`ss1') or somesuch) if `onezero' | (`plain' & `isifexp' & `usevars') { local s1 cond(`s1',1,0,`jsv') // marginally more compact for indicator, ensure plain expC==1 works if needed local onezero 1 } else local s1 cond(`s1',`s1',0,`jsv') //so as not to flatten non-indicators } } // end esourceF * } // end of arg-is-numeric else if `emiss' { // non numeric; might it be 'chunk' (e.q p+q sqrt(p) ) -- if it matters for e-mv capture confirm string variable `s1', ex if _rc!=0 { // not string, so is potentially `chunk' local j = length(`"`s1'"') local c = substr(`"`s1'"',1,1) //## check range if `"`c'"'!="." | `j'>2 { // to screen out .a etc -- local plainEVER 1 local plainTXT `"`s1'"' // just one exemplar } } } if `chat2'>=5 di as err "REPORT s1#`s1' cs1#`cs1' step#`step' plain#`plain' usevars#`usevars'" * if `isifexp' & `plain' & `usevars' & !`onezero' { //ensure that tests for zvarC==1 work * When evaluating simply "if fnC" we rely on any-number-T, but when using vars * need T=1; whereas expW uses merely fnW_mF * ## local plain 0 //treat not as simple plain if `emiss' local s1 cond(`s1',1,0,`s1') else local s1 cond(`s1',1,0,.) } * } // end is-arg } if `i'!=1 ERR197 `"`raw'"' 6 * local `what'fn `"`s1'"' local `what'fn_mF `"`s1_mF'"' * * _______________________________________________________ * local markE `" {hline 5}{c RT} "' if `plain' & `chat' { * local plainV 0 * local plainB = `onezero' & (`iswhen' | (`isifexp' & `usevars')) * local plainB = `onezero' & (`isifexp' & `usevars') // zvarC only place it matters local plainB = `onezero' & ((`isifexp' & `usevars')|`esourceF') // zvarC only place it matters xcpt if simplifies when sourcevar capture confirm numeric variable ``what'', ex if _rc==0 local plainV 1 // plain, numeric variable local plainSV = `esourceF' & `plainV' & `sourceD' // plain, Source, Numeric variable * ## if `plainSV'!=`sourceD' di as err "HERE HERE HERE HERE" local ptxt0 di as txt `"`markE'we\`ptxt4' place unmodified {c RT} {inp}``what'' {txt}{c LT} into local macro {res}{c 'g}fn`W''{txt}"' local ptxt5 di "`markE'{text}giving, in local macro{res} {c 'g}fn`W''{txt}, the valid function:" _n /// "`markD'(coded to preserve " _c local ptxt1 "a single" local ptxt2 "" local ptxt3 "" local ptxt4 "" * * if `plainB' & !`esourceF' { if `plainB' { // ## local ptxt1 "an indicator" local i "" if `inF' local i " there" local ptxt2 "`in' (coded 1/0`i')" if `isifexp'|`iswhen' local ptxt3 ", for tests," local ptxt4 " can" } di as txt _n " {inp}exp`W'{txt}{col 7}{c |} " _c if `plainV' { if `esourceF' & !`sourceD' di "is a variable with no missing values`in', so, despite {bf}source{sf}," else di "is `ptxt1' variable`ptxt2', so`ptxt3'" } else di as txt "contains no logical/relational operators, so" * if `isexp'|`isifnot'|`iselse' { // non-conditions if `plainSV' { // source emvs `ptxt5' di "source-tagged-missing)" TELL_COND `"``what'fn'"' 0 } else { // not plainSV `ptxt0' if `possibleF' { // an assert idiosyncracy * local expU `"`expfn'"' * local exp1q "" di "{space 6}{text}{c |} {err}Note: {text}since {inp}possible{txt} has been specified, " _n /// "{space 6}{text}{c |}{space 7}only cases where {it}exp{sf} is {res}false{txt} " /// "are deemed {res}contradictions{txt}" ULINE2 } // end possible else if `assert' { //plain assert * local expU `"`expfn_mF'"' * local exp1q "_mF" TELL_MF `""""' 2 TELL_COND `"`expfn_mF'"' 0 } else { // other non-cond if (`esourceF'|`extendedF') & !`plainV' di "`markD'(any extended-missing values likely smoothed to sysmiss)" ULINE2 } // end types of exp } // end plainSV switch } else { // conditions if `isifexp' & `emissC' & `plainV' & (!`plainB'|`esourceF') { // need track e-mv var last ## `ptxt5' if `esourceF' di "source-tagged-missing)" else di "extended-missing values)" TELL_COND `"``what'fn'"' 0 } else if `plainB' | !`usevars' | `iswhen' { `ptxt0' if `usevars' & !`iswhen' ULINE2 else { if `wide' TELL_WS `chat' //i.e. for functional wide only else { //not wide TELL_MF `W' 1 TELL_COND `"``what'fn_mF'"' 0 } } } else { di "`markE'{text}givingY, in local macro{res} {c 'g}fn`stepf''{txt}, the valid function:" _n /// `"`markD'(coded to constrain 'True' to return '1')"' TELL_COND `"``what'fn'"' 0 } } // end types } // end plain * the conds can be in generic, with the U assignments also * if !`plain' { // ## * * now report on this expression * report headers for expressions local plainSN 0 if `plain' & `esourceF' { capture confirm numeric variable ``what'', ex if _rc==0 local plainSN 1 // plain, Source, Numeric variable } if `plain' & (!`esourceF'|!`plainSN') & `chat' TELL_NLR `"``what''"' 0 `W' else { if `chat' { if `plainSN' { TELL_NLR `"``what''"' 1 `W' } else { TELL_RPN `W' forvalues j=1/`nrpn' { // `nrpn' is sections from preceeding if `j'==1 di " {hline 5}" _c else di "{space 5}>" _c di `"`rpn`j''"' di `"`rpnB`j''"' } } } } * if !(`plain' & (!`esourceF'|!`plainSN') & `chat') { local i 1 if `remind1' { // &| precedence local j = `remind1'+1 if `chat' TELL_PREC `remind2' `chat' 19 `"`markD'"' `j' local i 0 //flag header `chat' 19 `"`markD'"' `j'printed } if `remind2' & `chat' TELL_PREC 0 `chat' 19 `"`markD'"' `i' `"`mark2'"' `"`remind2a'"' `"`remind2b'"' * if `chat' { forvalues ii=1/`rmis' { // report treatment of explicit missings if any local ww "" if `ii'>1 local ww "further, " di as txt `"`markD'(`ww'relation '{inp}`rmisA`ii''{txt}' referencing '{res}`rmisB`ii''{txt}' will be treated literally"' _c if `rmisC`ii'' di as txt ";" _n "`markD' but, within that, sysmiss will {help validly##emvexp:still yield sysmiss})" else di ")" } di _col(7) "{c |} " _c TELL_MA `"`W'"' `emiss' `compactN' `esourceF' `esourceR' `everN' `everIND' local tail 0 if (`isifexp' & !(`wide' | `usevars')) | (`isexp' & `assert') | (`global' & `wide') | `iswhen' /// local tail 1 TELL_COND `"``what'fn'"' `tail' } } * if `isexp' & `assert' { // choose appropriate form of expfn; chat if `possibleF' { local expU `"`expfn'"' local exp1q "" if `chat' di "{space 6}{text}{c |} {err}Note: {text}since {inp}possible{txt} has been specified, " _n /// "{space 6}{text}{c |}{space 7}only cases where {it}exp{sf} is {res}false{txt} " /// "are deemed {res}contradictions{txt}" _n "{space 6}{c BLC}{hline 71}" } else { local expU `"`expfn_mF'"' local exp1q "_mF" if `chat' { TELL_MF `""""' 2 TELL_COND `"`expU'"' 0 } } } * if `iswhen' & `chat' { TELL_MF W 1 TELL_COND `"`whenfn_mF'"' 0 } * if `isifexp' { // decide, and report, on version of expC if `wide' | `usevars' { local exp2q "" local ifexpU `"`ifexpfn'"' if !`usevars' TELL_WS `chat' //i.e. for functional wide only } else { //use the m->F version of s1 we have been repeatedly making local exp2q "_mF" local ifexpU `"`ifexpfn_mF'"' if `chat' { TELL_MF `W' 1 TELL_COND `"`ifexpfn_mF'"' 0 } } } * } // end if !plain ## * * report where is choice of action for expressions * if `isexp' & `assert' { // choose appropriate form of expfn; chat if `possibleF' { local expU `"`expfn'"' local exp1q "" } else { local expU `"`expfn_mF'"' local exp1q "_mF" } } if `isifexp' { // decide, and report, on version of expC if `wide' | `usevars' { local exp2q "" local ifexpU `"`ifexpfn'"' } else { //use the m->F version of s1 we have been repeatedly making local exp2q "_mF" local ifexpU `"`ifexpfn_mF'"' } } * * reset flag for particular expression local remindTHIS 0 * and reset the counters in chat mode if `chat' { local remind1 0 // flag to count where &| precedence upsets LR sequence local remind2 0 // dito R logical local remind2a "" local remind2b "" } } // ]] end of foreach action * ________________________________________________________________________________________ * ________________________________________________________________________________________ * ________________________________________________________________________________________ * Note: editor does not see this as the matching closing bracket * but a. logic says it is * and b. if program is ended here and bracket added/removed the interpreter * reports an error * so I think the attributionis correct * * Task is now executing and reporting. * cond coded xxxx is in xxxxfn and xxxxfn_mF * (the _mF form redundant for some xxxx) * * __________________________________ * * G Execute compiled function(s) or make macro * __________________________________ * * reportd precedences in nonchat if !`chat' { local k = `mark3'+10 if `remind1' { // &| precedence local j = `remind1'+1 TELL_PREC `remind2' `chat' `k' `"`mark'"' `j' `"`mark2'"' local i 0 //flag header `chat' 19 `"`markD'"' `j'printed } if `remind2' TELL_PREC 0 `chat' `k' `"`mark'"' `i' `"`mark2'"' `"`remind2a'"' `"`remind2b'"' if `remindEXP' { `marku' // so that closing line happens local j = word("`numer'",`remindEXP') if `remindEXP'>1 di "`mark2'{col `k'}(this reminder covers `j' expressions)" else if `ifexpF'|`whenF' di "`mark2'{col `k'}(only one expression affected)" } } * if `global' { // global (subsumes wrap) takes precedence * * rely on Stata zapping missing->true in widely, else force missing to false in conditional if !`wide' & (`globalIF'|`wrap') local s1 `"`s1_mF'"' * global `var' `s1' if `chat' { di as txt _n "{space 6}{c TLC}{hline 70}{c TRC}" if `globalIF'|`wrap' { di "{txt}{space 6}{c |} The conditional:{col 78}{c |}" _n "{space 6}{c |}{space 5}{res}if {c 'g}fn" _c if !`wide' di "_mF" _c di "{res}'{txt}{col 78}{c |}" _c } else di as txt "`markD'That function (coding for {bf}T, F{sf} and {ul:missing}){col 78}{c |}" _c if `wrap' { di _n `"{space 6}{c |}{txt} is placed in macro {res}{c 'g}ifcond'{txt}"' _c if `everN' di "{col 78}{c |}" else di ", and also returned in {help validly##saved:{bf}r(ifcond){sf}}{col 78}{c |}" } else { di _n "{txt}`markD'is returned in the global macro {res}{c S|}`var'{txt}{col 78}{c |}" if `globalIF' { di "{txt}`markD'to be used, with a command, by typing: {inp}.{it}command{sf} {c S|}`var'{txt}{col 78}{c |}" if !`wide' TELL_NV `"`markD'"' } else { di "`markD'to be used as required{col 78}{c |}" TELL_NC "`var'" `"`exp'"' `"`markD' "' } } } if !`wrap' { // i.e. global if `everN' { if `chat' di "`markD'" _c else di "`mark'" _c di "{err}{it}note{sf}:{txt}" _c di " coded assuming that{inp}" _c local j 0 forvalues i= 1/`metN' { if !(`metVmv`i'' + `metVemv`i'') { local jj "" if `j' local jj "," di "`jj' `metV`i''" _c local ++j } } if `chat' di _n "{txt}`markD'" _c else di _n "{txt}`mark'" _c di "{space 6}shall {help validly##globwarn:continue to have {ul:no} missing} values" _c if `chat' di "{col 78}{c |}" else di } if `chat' di as txt "{space 6}{c BLC}{hline 70}{c BRC}" if `globalIF' global `var' `" if `s1'"' else if !`wrap' global `var' `s1' if !`chat' ULINE `mline' `mark3' return local RPN `returnRPN' exit // global done } if `wrap' { local condition `" if `s1'"' if `chat' di as txt "{space 6}{c BLC}{hline 70}{c BRC}" * next 3 lines kludge to get length local ii = subinstr(`"`zero'"',`"if `ifexp'"',`"xifcondx "',1) local n= length(`" Executing .`by'`ii'"') if !`chat' ULINE `mline' `mark3' di _n `" {txt}Executing{inp}{space 2}.`by'"'subinstr(`"`zero'"',`"if `ifexp'"',`"{c 'g}ifcond' "',1) "{txt} {c |}" /// _n " {hline `n'}{c BRC}" local zero = subinstr(`"`zero'"',`"if `ifexp'"',`"\`condition' "',1) //all spaces single by now * note `condition' must be entered "suppressed" in substitution, else it becomes too long a string if `chat2' di as err _n `" EXECUTING: `by'`zero'"' _n return local RPN `returnRPN' // the RPN condition if `remindF' return local precedence `precedence' local i WITHHELD: nonmiss-specific code if `everN' return local ifcond `i' else return local ifcond `condition' `by'`zero' * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ return add // to preserve any r() returns from wrapped command return local RPN `returnRPN' // repetition so that return even if wrapped command fails if `everN' return local ifcond `i' else return local ifcond `condition' if `remindF' return local precedence `precedence' * exit } } // ]]end global else { // ## * if `emissC' &`chat' /// di _n " {txt}Since we are generating a fresh variable, {inp}`var'{txt}, we can flag " _n /// " when {bf}`var'{sf} {help validly##extended4:becomes undefined {ul:because}} " /// "{bf}{c 'g}fnC'{sf} is a specific {ul:extended}-missing value; " _n /// " by setting the result to that extended-missing code." * * test size and warn? TESTS X =`expfn' if `wide' TESTS C =`ifexpfn' else TESTS C_mF =`ifexpfn_mF' if `ifnotF' TESTS N =`ifnotfn' if `elseF' TESTS E =`elsefn' * * make a copy of our target variable, to allow summary reporting if `repF' { tempvar prevar quietly generate `prevar' = `var' quietly compress `prevar' } local qt "quietly " if `chat' local qt "" * if `chat' { //## not only usevars local i "" if `impute' { // expand if `genF' local i " gen" else local i " {err}replace{inp}" } else { // turn rep into replace local j = substr(`"`raw'"',1,4) if "`j'"=="rep " local raw = subinstr(`"`raw'"',"rep ","replace ",1) } di _n "{c TLC}{hline 76}{c TRC}" _n "{c |} evaluating:" _n /// `"{c |} {inp}.`by'validly`i' `raw'{txt}"' } * * if `usevars' { // ## check whether want to keep refers for chat if `chat' di as txt "{c |}" if `chat' di "`col23A'{c TLC}{hline 3}" _n /// "{c |}{col 8}{txt}temp workspace {c RT} " _c local secondtemp 0 if `ifexpF' { tempvar zvarC if `chat2' di as err _n `" EXECUTING: `by'gen `zvarC'=`ifexpfn' `in' "' _n quietly `by'gen byte `zvarC'=`ifexpfn' `in' //we need to fix the condition for repeat replace calls if `chat' { di "{inp}.`by'gen byte zvarC = {c 'g}fnC'`intext'" local secondtemp 1 } } if `whenF' { // when() stores for calls tempvar zvarW quietly `by'gen byte `zvarW'=`whenfn_mF' `in' // quietly sum `zvarW' `in' if r(min)==r(max) local whenCONS = r(min)+1 //tell user if when is a constant if `chat2' di as err _n `" EXECUTING: `by'gen `zvarW'=`whenfn_mF' `in' "' _n if `chat' { if `secondtemp' di "`col23'" _c //cant use LIN since two spaces di "{inp}.`by'gen byte zvarW = {c 'g}fnW_mF'`intext'" } if `ifexpF' { local whenTXT " & zvarW" local whenVAR `" & `zvarW'"' } else { // 'when' without 'if' local whenTXT "zvarW" local whenVAR `"`zvarW'"' } } if `emissC' { // store fn for extend-missing in if tempvar zvar if `chat2' di as err _n `" EXECUTING: `by'`zvar'=`expfn' `in' "' _n quietly `by'gen `zvar'=`expfn' `in' * NOTE we could save CPU time by inserting `whenVAR' here, but at the risk * of tilting the evaluation into the "too long" category; since expcon is * possible locus of too-long have opted to squander CPU in return for * more-robust if `chat' di "`col23'{inp}.`by'gen zvar{space 2}= {c 'g}fn'`intext'" } if `chat' & `refers' COMFORT `var' * if !`ifexpF' local wideR "" else if `wide' local wideR "!=0" else local wideR "==1" } * if `chat' { if `usevars' { LIN 3 di "{txt}{c LT}{hline 4} main expression {c RT} {inp}." _c } else di "{txt}{c |}" _n "`col23A'{c TLC}{hline 3}" _n /// "{txt}{c LT}{hline 4} using the above {c RT} {inp}." _c local i " " local j "" if `repF' { local i "{space 2}" if `impute' local j "{err}{bf}" } if `usevars' { if `ifexpF' local k zvarC else local k "" // usevars no-if always has active when if `emissC' di "`action'`type'`i'`var'{sf}`vlab'{inp}`equals' zvar{space 2}if `k'`wideR'`whenTXT'`intext'`nop'" else di "`by'`j'`action'{sf}{inp}`type'`i'`var'`vlab'{inp}`equals' {c 'g}fn'{space 2}if `k'`wideR'`whenTXT'`intext'`nop'" if `wide' { LIN 2 _c di "with option {bf}widely{sf}, using '{inp}!=0{txt}' instead of '{bf}==1{sf}'" } } else { // functional form if `ifexpF' local k `" if {c 'g}fnC`exp2q''"' else local k "" di "`by'`j'`action'{sf}{inp}`type'`i'`var'`vlab'{inp}`equals' {c 'g}fn`exp1q''`k'`intext'`nop'" if `assert' di "{txt}{c BLC}{hline 21}{c BT}{hline 54}{c BRC}" } } * if `assert' { //just pretifying the output from assert local iftxt "" if `ifexpF' local ifTXT `" if `ifexpU' "' capture `by' assert `expU' `ifTXT' `in'`assertOPN' if _rc!=0 & `chat' di _n _col(11)"{txt}Note: RPN parsing returned in {res}r(RPN){txt}" di as txt _n " Finding, {ul:on that interpretation}, " _c if _rc==0 di "no contradictions." else if "`fast'"!="" di "that {err}the " _c * * if `chat2' di as err _n `" EXECUTING: `by' assert `expU' `ifTXT' `in'`assertOPN'"' _n `qta' `by' assert `expU' `ifTXT' `in'`assertOPN' * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ exit } * * if `usevars' { local tidy = !`asstring'&`genF' // to tidy main generate reporting, only works for numeric if `chat' local vert "{txt}{c |}" * if `emissC' { if `chat2' di as err _n `" EXECUTING `action'`type' `var'`vlab' `equals' `zvar' if `zvarC'`wideR'`whenVAR' `in'`nop'"' _n if `chat' LIN 2 _c local qt2 "" if `genF'|(`repF' & !`chat') local qt2 "quietly " `qt2'`action'`type' `var'`vlab' `equals' `zvar' if `zvarC'`wideR'`whenVAR' `in'`nop' // 'by' irrelevant since here vars * since gen itself sometimes does sometimes does not generate output, we make our own if `genF' & `chat' REPORT `var' `asstring' `chat' `repF' 0 1 `prevar' * but suppressing here the 'extended' output for consistency with following 'replaces' if `chat' di "{txt}{c LT} noting e-m in {c 'g}fnC' {c RT} {input}.replace `var'`equals' zvarC " /// "if zvarC>. & (zvar<. | zvar==zvarC)`whenTXT'`intext'`nop'" /// _n "`col23' " _c if `chat2' di as err _n `" EXECUTING replace `var' `equals' `zvarC' if (`zvarC'>.) & (`zvar'<. | `zvar'==`zvarC')`whenVAR'`in'`nop'"' _n `qt' replace `var' `equals' `zvarC' if (`zvarC'>.) & (`zvar'<. | `zvar'==`zvarC')`whenVAR'`in'`nop' if `chat' di `"`col23' (these would be "changes from {it}sysmiss{sf} to {ul:extended}-missing")"' if `sourceR' { * if `chat' di _col(10) "{txt}In addition to the {help validly##sourcecode:coded random tie-breaking}, we need, for that" _n /// * _col(10) "e-mv replace{space 2}{c RT}{inp} .replace `var' = cond(int(2*runiform()),zvar,zvarC) if (zvar>. & zvarC>.)" /// * " & (zvar!=zvarC)`whenTXT'`in'`nop'{txt}" _n _col(10) _c if `chat' di "{txt}{c LT} random tie-breaking {c RT} {input}.replace `var' = cond(int(2*runiform()),zvar,zvarC){space 2}///" /// _n "{txt}{c |} for e-mv in {c 'g}fnC'{space 3}{c |}{space 13}{inp} if (zvar>. & zvarC>.) & (zvar!=zvarC)`whenTXT'`intext'`nop'{txt}" /// _n "`col23' " _c if `chat2' di as err _n `" EXECUTING `qt' replace `var' = cond(int(2*runiform()),`zvar',`zvarC') if (`zvar'>.&`zvarC'>.) & (`zvar'!=`zvarC')`whenVAR'`in'`nop'"' _n `qt' replace `var' = cond(int(2*runiform()),`zvar',`zvarC') if (`zvar'>.&`zvarC'>.) & (`zvar'!=`zvarC')`whenVAR'`in'`nop' if `chat' di `"`col23' (these would be "changes from {it}sysmiss{sf} to {ul:extended}-missing")"' } } else { // not emissC if `chat2' di as err _n `" EXECUTING `by'`action'`type' `var'`vlab'`equals' `expfn' if `zvarC'`wideR'`whenVAR' `in'`nop' "' _n if `chat' di as txt "`col23' " _c local qt2 "" if `genF'|(`repF' & !`chat') local qt2 "quietly " `qt2'`by'`action'`type' `var'`vlab'`equals' `expfn' if `zvarC'`wideR'`whenVAR' `in'`nop' if `genF' & `chat' REPORT `var' `asstring' `chat' `repF' 0 1 `prevar' } } else { // not usevars computation, explicit function call if `repF' { tempvar prevar quietly generate `prevar' = `var' } * ## size checks? if `chat2' di as err _n`" EXECUTING `by'`action'`type' `var'`vlab'`equals' `expfn'`ifTXT' `ifexpU'`in'`assertOPN'`nop'"' _n quietly `by'`action'`type' `var'`vlab'`equals' `expfn'`ifTXT' `ifexpU' `in'`assertOPN'`nop' * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ } * if `ifnotF' { local i 0 if `chat' { di "{txt}{c LT}{hline 1} implementing {bf}{ul:ifnot}{sf} {c RT} {input}.`by'replace `var'`equals' {c 'g}fnN' if " /// "zvarC==0`whenTXT'`intext'`nop'{txt}" _n "`col23' " _c } if `chat2' di as err _n `" EXECUTING: `by'replace `var' `equals' `ifnotfn' if `zvarC'==0`whenVAR'`in'`nop'"' _n `qt'`by'replace `var' `equals' `ifnotfn' if `zvarC'==0`whenVAR'`in'`nop' * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ } if `elseF' { local elseT ">=." if `elseALL' local elseT "!=1" //needed both in chatty and non chatty modes if `chat' { di "{txt}{ c LT}{hline 2} implementing {bf}{ul:else}{sf} {c RT} {input}.`by'replace `var'`equals' {c 'g}fnE' if " /// "zvarC`elseT'`whenTXT'`intext'`nop'{txt}" _n "`col23' " _c if `elseALL' di "(no {bf}ifnot{sf}, so capturing false {ul:and} {it}indeterminate{sf})" /// _n "`col23' " _c } if `chat2' di as err _n `" EXECUTING: `by'replace `var' `equals' `elsefn' if `zvarC'`elseT'`whenVAR'`in'`nop'"' _n `qt'`by'replace `var' `equals' `elsefn' if `zvarC'`elseT'`whenVAR'`in'`nop' * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ } * if `chat' { // note various reports * if `usevars' { LIN 3 di as txt "{c |}{col 8}temp workspace {c +}" /// " {bf}cleared{sf} ({it}workspace variable-names were fictitious{sf})" } * if `extdefF1'|`extdefF3' { if `extdefF1' di as txt "`chatnote'for this command," else di as txt "`chatnote'with a target string variable," LIN 2 _c di " the default '{bf}extended:on{sf}' setting has been suppressed" } if `extdefF2' di as txt "`chatnote'option {bf}extended{sf} otiose, since already set as default" if (`equivalentF1' | `equivalentF2') { di as txt "`chatnote'we would get the identical result, without {bf}when{sf}, by:" LIN 2 _c if `equivalentF1' di `" {bf}.vy `dots' if (`ifexp') & (`when'){sf}"' else di `" {bf}.vy `dots' if `when'{sf}"' } * if `noneede' & `chat' di as txt "`chatnote'{help validly##sourcecode:no need to specify {bf}{ul:e}xtended{sf}} " /// "in addition to {bf}{ul:s}ource{sf}" * } // end pure chat notes * if `repF' & (`elseF'|!`ifexpF') & !`inF' & (!`whenF' | (`whenF' & `whenCONS'==2)) { if `chat' di as txt "`chatnote'variable {bf}`var'{sf} now {ul:completely} replaced." local repALL 1 //used below, so not within chat } * if `whenCONS' { local i "not-" if `whenCONS'==2 local i "" if `chat' di "`chatnotee'" _c else { di "`mark'{err}{it}note{sf}:{txt} " _c `marku' } di `"the '{bf}when{sf}' condition, {inp}`when'{txt}, was uniformly {bf}`i'True{sf} `in'"' if `chat' { LIN 2 _c if `whenCONS'==1 di " so the {bf}`action'{sf} command had {bf}no{sf} effect." else di " so added no constraint." } } * if `warn_max'|`warn_min' { local i "min" if `warn_max' local i "max" if `chat' di "`chatnotee'" _c else { di "`mark'{err}note{txt}: " _c `marku' } di "{help validly##max:click here for a caution} on the interpretation of {inp}`i'(){txt}" } * if `stringTRIP' & ("`stringEMV'"!=".s") & !`sourceF' { // source reports this in table if `chat' di as txt "`chatremind'" _c else { di as txt "`mark'" _c `marku' } di "indeterminate string relations return {bf}`stringEMV'{sf}" } * if `extendedF' { if `arithopF' { REMINDA 1 `arithopC' `chat' "`mark'" // a reminder about arithmetic misstreatment of missing `marku' } * inelagntly, thse are seperate tests - the arithop tested characters, thy plainever is set * by any plain chunk. Rescue by only reporting 2nd if it is nonarithmetic; not exact, but will do if `plainEVER' { local k 1 foreach i in + * / - ^ { if strpos("`plainTXT'","`i'") { local k 0 continue, break } } if `k' |!`arithopF' { REMINDA 0 `plainTXT' `chat' "`mark'" // a reminder about function misstreatment of missing `marku' } } } * if `remindEXP' & `chat' { local j = word("`numer'",`remindEXP') if `remindEXP'== 1 local i "" else local i "s" di as txt "`chatremind'{help validly##precedence:precedence sequence inversion`i'}{txt} in the parsing of" LIN 2 _c di " `j' expression`i' (see above)" } * if `sourcewarnF' { if `chat' { di "{c |}{col 15}{err}{bf}warning{sf}{txt} {c +} with {bf}{ul:s}ource{sf}" /// " set, explicit reference to {bf}" _c forvalues ii=1/`rmis' { di " `rmisB`ii''" _c } di "{sf}" LIN 2 _c di " may not have worked as you intended, since" LIN 2 _c di " extended-missing codes {help validly##sourceimp:{ul:within} the data are not visible.}" } else di "`mark2'{err}{bf}warning{sf}:{txt} on use of '{inp}`sourcewarnEMV'{txt}' in this context;" /// "{help validly##sourceimp: click here for discussion}" } * if "`vlabND'"!="" & `chat' { di "`chatnotee'{help validly##extype:value label} '{inp}`vlabND'{txt}'" /// " has yet to be defined" } * if (`warn_mi'|`warn_mv') { WARN_MI `warn_mi' `chat' "`mark'" `marku' } * if `repF' & `sourceF' & !`repALL' { // look to see whether codes preused * taking care this code happens *before* final REPORT, which modifies prevar capture misstable sum `prevar' if `prevar'==`var' //both untouched and changed-to-match * better just untouched, but no way to flag local prevEMV =r(N_gt_dot) if "`prevEMV'"=="." local prevEMV 0 local total 0 if `prevEMV' { // emvs, are they sourced tempvar hunt * count number of each source foreach emv of local sourceXM { quietly gen byte `hunt' = 1 if `prevar'==`emv' & `prevar'==`var' capture misstable sum `hunt' if `prevar'==`var' local i = r(N_lt_dot) if "`i'"=="." local i 0 if `i' local ++sourceprev local total = `total'+`i' capture drop `hunt' } local prevEMV = `prevEMV' - `total' if `prevEMV' local prevEMV 1 } } if `repF'|`genF' { //final summary local i 2 if `chat' { LIN 3 di as txt "{c LT}{hline 6} final summary {c RT} " _c local i 0 } else { if `"`mark'"'==`"`mark2'"' ULINE `mline' `mark3' di "{col `mark3'}" _c } REPORT `var' `asstring' `chat' `repF' `extendedF' `i' `prevar' if `chat' di "{c |}{col 20}{hline 3}{c BT}{hline 3}" } * local skip 0 if `extendedF' { // report on extended values, iff appropriate TELL_EM "`var'" "`in'" `chat' `genF' `ifnotF'|`elseF' `"`whenRAW'"' `zvarW' if `r(skip)' local skip 1 // no point in reporting table } * } else if 3==4 { //exp only ## local qta "quietly" if `assert' { //just pretifying the output from assert di as txt _n " Finding, {ul:on that definition}, " _c capture `by'`action' `var' `equals' `s1' `in'`assertOPN' if _rc==0 di "no contradictions." local qta "" } if `repF' { tempvar prevar quietly generate `prevar' = `var' } * if `chat2' di as err _n `" EXECUTING: `by'`action'`type' `var'`vlab' `equals' `expfn' `in'`assertOPN'`nop'"' _n `qta' `by'`action'`type' `var'`vlab' `equals' `expfn' `in'`assertOPN'`nop' * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ } * if `chat' local vert "{c |}" * * * if `sourceF' { // report for source if `sourceCHAR'==97 di as txt "`vert'" _n "`vert' {txt}{it}note{sf}:" /// " no relevant raw variables (with missing values) were found as logical/relational" /// _n "`vert'" _col(10)"operands (or as simple expressions), so option {bf}source{sf} is here inefficacious." else if `skip' { if `chat' di as txt "{c |} and, for the same reason, the table of 'source' codes is immaterial" } else { if `chat' { di as txt "{c |}" _n "{c LT} " _c local jpos `vert'{col 8} } else { local jpos {col `mark3'} di _n "`jpos'" _c } di "{txt}Source of missing values (conflicts " _c if `sourceR' di "{help validly##rscode:broken randomly})" else di "set to {it}sysmiss{sf})" local i 1 di "`jpos'{c TLC}{hline 4}{c TT}{hline 17}{c TRC}" _n /// "`jpos'{txt}{c |}e-mv{c |} Source variable {c |}" _n /// "`jpos'{c LT}{hline 4}{c +}{hline 17}{c RT}" foreach vin of local sourceVAR { local vinm : word `i' of `sourceXM' local ++i di "{txt}`jpos'{c |} {res}`vinm' {txt}{c |}{inp} `vin'" } if `stringTRIP' di "{txt}`jpos'{txt}{c |} {res}`stringEMV' {txt}{c |}[string vars]{space 4}{c |}" if !`sourceR' & `i'>2 { di "{txt}`jpos'{txt}{c |} {res}. {txt}{c |}[conflict]" _c if `genF' & ((`ifexpF' & !`elseF') | (`whenF' & `whenCONS'!=2)) /// di "{bf}{c |}{sf}{it}not-generated{sf}" else di "{space 7}{c |}" } di "{txt}`jpos'{c BLC}{txt}{hline 4}{c BT}{hline 17}{c BRC}" if `isNVAR' { di "`jpos'variable" _c if `isNVAR'>1 di "s" _c foreach j of local sourceNVAR { di " {inp}`j'{txt}" _c } di " had no missing values`in'" } if `i'==2 di "`jpos'(odd to invoke {bf}source{sf} for only one pertinent variable?)" local j 0 if `sourceprev' & !`repALL' { if `sourceprev'<6 local sourceprev = word("`numer'",`sourceprev') di "`jpos'{err} note{txt} variable {inp}`var'{txt} " /// "{help validly##varsource:already held `sourceprev' of these e-mv codes}" local j 1 } if `prevEMV' & !`repALL' { if `j' di as txt "`jpos'{space 6}and also" _c else di "`jpos'{err} note{txt} {inp}`var'{txt}" _c di as txt " contains other e-mv codes, predating the current command." } } } if `chat2'>=5 di as text _n " End: `vvers' `vdate'" if `chat' { if !`global' { di as txt "{c |}" _n "{c |} {txt}RPN parsing returned in {res}r(RPN){txt}" /// "{space 4}To view it again, type: {inp}.vy{txt}" di as txt "{c BLC}{hline 76}{c BRC}" } } * return local RPN `returnRPN' if `remindF' return local precedence `precedence' * ************************************************************************************ ************************************************************ END main program ****** ************************************************************************************ end * * * * ***************************************************** * Subprograms, of varying importance and complexity. * In alphabetic rather than 'functional' order: ***************************************************** program CANT args a b di _n `" {txt}'{inp}`a'{txt}' does not make sense with '{inp}`b'{txt}'; "' /// _n "{err} these " _c error 184 end program CHECKEXP args putative asstring what * checks whether the first arg is a valid expression * local i `asstring' if `asstring' { //exp1 was a string, so this has to be local 0 = `"=(`putative')=="c""' //remmake as string-equivalence assignment capture syntax =/exp //to allow location report FINGER `what' syntax =/exp } else { // numeric local 0 = `"=`putative'"' capture syntax [=/exp ] FINGER `what' syntax [=/exp ] } end program COMFORT args var di as txt "{c |}{col 23}{c |} for reassurance: though {c 'g}fnC' itself references {bf}`var'{sf}," _n /// "{c |}{col 23}{c |} use of zvarC prevents side effects in sequential changes below" end program COMPACT, rclass args fn chat2 what * to assemble the evaluation of alternate codings * Note: cant use string operators since exps will often by too long for strings * so options restricted. * * count cond( global validly_t_m_p_ `"`fn'"' // must use a global global validly_t_m_p_ : subinstr global validly_t_m_p_ "cond(" "(", count(local count) all * * count operators local operators 0 local list "== >= <= != > < * + / ^" //note conjoint first foreach z of local list { global validly_t_m_p_ : subinstr global validly_t_m_p_ "`z'" "", count(local n) all local operators = `operators'+`n' } * scrub remaining non-bracket forvalues i = 32/255 { if `i'!=96 & `i'!=40 & `i'!=41{ // 96 is ` which messes up if included if `i'==32 local j " " else local j=char(`i') //assign char(32) seems not to work global validly_t_m_p_ : subinstr global validly_t_m_p_ `"`j'"' "", all } } * count bracket depth and pairs local pairs 0 forvalues i = 1/30 { local depth = `i'-1 local n : length global validly_t_m_p_ if `n'==0 continue, break global validly_t_m_p_ : subinstr global validly_t_m_p_ "()" "", count(local n) all local pairs = `pairs'+`n' } macro drop validly_t_m_p_ //clean up * chatter if `chat2'>=5 { di as txt `"# Coding `what', conds `count', depth `depth', pairs `pairs', operators `operators':"' TELL_COND `"`fn'"' 0 } if `"`what'"'=="err" { //error call from TESTS di _n "For information: it embodies `count' calls to {bf}cond{sf};" /// _n _col(18) "also `pairs' pairs of brackets, in places `depth' deep;" /// _n _col(18) "further, it contains `operators' dyadic operators." _n exit } * reporting conds and depth for action: return scalar conds = `count' return scalar depth = `depth' end program DEFAULT global validlyDefault /// "detail:OFF extended:OFF impute:ON compact:ON nullstring:.s remind:ON nomiss:ON chat2:0 " end program DEFVAL, rclass args key * calling program knows if answer is OFF/ON local default = substr("$validlyDefault",strpos("$validlyDefault","`key':")+3,3) if "`default'"=="OFF" local default 0 if "`default'"=="ON " local default 1 // other flags passthru as are local default = trim("`default'") return local default `default' end program ERR197 args raw n di as err _n(2)"{ul:PLEASE}:" di " {help validly##contact:email the author}" _n " listing the ERROR SUB-CODE {inp}`n'{err}" di " and the precipitating command line:" di as inp `" .`raw'"' _n di as err " {hline 3} it is possible that this particular" _n /// " error may be ascribable to the author's " _c error 197 end program FINGER args where * to allow notification of exp errors in ifnot|else|when if _rc!=0 di _n "{err}Error in {inp}`where'(){err} {hline 2} " _c end program GEN_TV args varlist extendedF nonindF * program to generate test variables * Essentially a stand-alone utility whose internal logic * has NO connection to the rest of program validly. local arraymax 60000 // max array considered local nv 0 foreach var in `varlist' { local ++nv if `nv'==1 local var1 `var' if `nv'==2 local var2 `var' } if `nv'==1 error 102 local b 1 // active bite local bs 3 //bitesize if `extendedF' local bs 5 * local m =`bs'^`nv' if `m'>`arraymax' { if `extendedF' di "With 'extended' set, " _c di "that would generate `m' patterns; " _c error 103 } local ms = _N * local ma `m' //array size default if `ms'>0 { // extant data if `m'>`ms' { local i = `ms'+1 di " {err}{ul:Note}:{txt} This would require addition of empty rows, {res}`i'{txt} to {res}`m'{txt}, across" /// _n " the other variables already in the data. {err}Continue ({bf}y{sf}/{bf}n{sf}) {bf}?{sf}{txt}" _r(validly_t_m_p_) if ltrim("$validly_t_m_p_")=="y" | ltrim("$validly_t_m_p_")=="Y" { di as txt " continuing:" macro drop validly_t_m_p_ } else { di as err " User termination." macro drop validly_t_m_p_ exit } } else local ma `ms' // datat bigger than needed } * quietly set obs `ma' * * these few lines do the work: foreach var in `varlist' { local bd `b' local b = `b'*`bs' quietly generate `var'=. local r1 1 while `r1' < `m' { local r2 = `r1' + `b' -1 quietly replace `var' = floor((_n-`r1')/`bd') in `r1'/`r2' local r1 = `r2'+1 } quietly recode `var' (0=1) (1=0) (2=.) (3=.a) (4=.b) if `nonindF' quietly replace `var' = 1+int((100)*runiform()) if `var'==1 } * * more talk: if `nonindF' local i "" else local i " (one)" di as txt _n "{hline}" _n " Generating all possible patterns of the values{res} {bf}True`i'{sf}, {bf}False (zero){sf}, {it}sysmis" _c if `extendedF' di ", .a{sf}, and {bf}.b{sf} {txt}" else di "{sf}{txt}" di " across variables: {inp}{bf}`varlist'{sf}{txt}; so, making {res}`m'{txt} distinct patterns." /// _n " (leftmost variables vary most rapidly)" if `nonindF' di `" (variables are NOT "indicator variables":"' /// _n `" observations evaluated True contain random integers, range 1-100)"' else di `" (variables are "indicator variables")"' if `m'<`ma' di " (Note: patterns in rows 1 to `m' only)" * guidance: local i 2 di _n " To examine all patterns" while `i'<=`nv' { di _col(5) "{hline 7} across" _c local ir = `nv'-`i'+2 local j 1 foreach var in `varlist' { if `j'>1 { if `j'==`ir' di " and" _c else di "," _c } di " {inp}{bf}`var'{sf}{txt}" _c local ++j if `j'>`ir' continue, break } local r2 = `bs'^`ir' if `r2'==`ma' di ": use all observations" else di `": specify "{res}{bf}in 1/`r2'{sf}{txt}""' local ++i } if `nv'>2 { di _col(7) "{ul:Note}: the" _c if `m'<`ma'|`nv'>3 di "se ranges apply only to these specific sets" else di" range applies only to that specific subset" } di _n " {ul:Example}" _n /// giving example " Using {inp}`var1'{txt} and {inp}`var2'{txt}, you could compare the results from:" /// _n _col(9) "{inp}{bf}.gen ys = `var1' | !`var2' {sf}" _c local r2 = `bs'^2 if `r2'<`ma' di "{bf}in 1/`r2'{sf}{txt}" else di " {txt}" di " (perhaps modifying by some tests for missing values????)" _n /// " with the correct ones from:" _n /// _col(9) "{inp}{bf}.validly gen yv = `var1' | !`var2'{sf}" _c if `r2'<`ma' di " {bf}in 1/`r2'{sf}" _c if `extendedF' di ", {sf}extended{txt}" else di "{txt}" di "{hline}" end program LIN args n cont * if `n'==3 di as txt "{c |}{col 23}{c LT}{hline 3}" else if `n'==2 di as txt "{c |}{col 23}{c |} " `cont' else di as txt "{c |} * end program MIXED args n vvers fn * p1=1 iff function (function in p3), ==2 iff arithmetic * p2 is version di _n "{txt} {ul:Apologies}:{err} `vvers' of {bf}validly{sf} does not handle " if `n' { di as err " {ul:arithmetic} carried out on the {ul:{bf}RESULTS{sf}} of logical/relational expressions," _n /// " as in expression: {inp}`fn'{txt}" } else di as err " logical/relational operators {bf}WITHIN{sf} a function such as {ul:`fn'({c 133})} " di as txt _n " {ul:Suggested Workaround:}" if `n' di " First, check that you meant to make that exact request" _n /// " (though admittedly definable, it is still a mite substantively odd" _n /// " to need to do arithmetic on things which are True or False?);" _n /// " if you did mean it, then:" _n di " Use {res}validly generate{txt} to make new variable(s)," _n /// " correctly evaluating the {bf}relevant{sf} logical/relational expression(s)" _c if `n' di "" else di " within {inp}`fn'({c 133})" local i "" if `n' local i " for arithmetic" di as txt " And then reissue your current command" _n /// " using the new variable(s)`i' in place of the relevant expression(s)" _c if `n' di "" _n(2) " {help validly##restrictions:click here}" _c else di " within {inp}`fn'({c 133})" _n(2) " {help validly##restrict2:click here}" _c di as txt " for more information" _n error 696 end program NEEDS args a b di as txt _n `"'{inp}`a'{txt}' presupposes '`b''; "' _c error 119 end program NOEGEN args mark mark2 di as txt "`mark'apologies, but {err}validly does not operate on egen{txt}" _n(2) /// "`mark2'{ul:Suggestion}:" _n "`mark2' Turn the relevant logical/relational expression(s) into valid function(s)" _n /// "`mark2' using {help validly##global:{bf}vy global mname exp{sf}} {ul:or}" /// " {help validly##global:{bf}vy global mname if exp{sf}} as appropriate" _n /// "`mark2' and invoke {bf}egen{sf}, embeding these macros where relevant." _n error 133 end program PLAIN1 args exp action di _n `" {res}Note{txt} Since {c RT} `exp' {c LT} uses no logical/relational operators,"' /// _n " a plain {inp}`action'{txt} would here give the same result." _n end program REMINDA args n arithopC chat mark if `chat' { di as txt "{c |}{col 14}{it}reminder{sf} {c +} Stata's " _c if `n' di "{help validly##extendedn2:arithmetic operators discard {ul:extended}-missing}" else di "{help validly##extendedn2:functions mostly smooth {ul:extended}-missing to {it}sysmiss{sf}}" LIN 2 _c di " for eaxmple, {bf}" _c if `n' { di "4`arithopC'(.a){sf} is seen as {it}sysmiss{sf}, not correctly as {bf}.a{sf}" LIN 2 _c di " so {ul:extended} preserved only for logical/relational operators." } else di "`arithopC'{sf} may return only sysmiss for missing." } else { di "`mark'{it}note{sf}: " _c local i "does" if `n' di "arithmetic (e.g. '{bf}`arithopC'{sf}')" _c else { di "function '{bf}`arithopC'{sf}'" _c local i "may" } di " {help validly##extendedn2:`i' not preserve extended-missing codes}" } end program REPORT args var asstring chat repF extendedF brace prevar * print summary info on a varianle * prevar last argument since may be absent * * enter knowing either generate or replace * follow Stata convention of report-is-over-ALL-of-variable * note catering for strings (none of which come extendedF) local bs "" local bf "" if `brace' { local bs "(" local bf ")" } if `repF' { if `asstring' { tempvar varmiss // misstable does not work on strings, so we do: quietly gen byte `varmiss' = cond(`prevar'==`var',1,cond(mi(`var'), ., .a)) * so 1 is no-change, .a is changed-to-value, . is changed-to-any-missing capture misstable sum `varmiss' //. no qualifiers, cause Stata reports over all drop `varmiss' } else { // numeric quietly replace `prevar' = cond(`prevar'==`var',1,cond(mi(`var'), ., .a)) quietly compress `prevar' * so 1 is no-change, .a is changed-to-value, . is changed-to-any-missing capture misstable sum `prevar' //. no qualifiers, cause Stata reports over all } local tonum=r(N_gt_dot) if "`tonum'"=="." local tonum 0 local tomi=r(N_eq_dot) if "`tomi'"=="." local tomi 0 local tot = `tonum'+`tomi' local toemv 0 if `tomi'>0 & `extendedF' { // (strings are not extended) quietly replace `prevar' = cond(`prevar'==.,cond(`var'>.,.a,.),1) capture misstable sum `prevar' //. no qualifiers, cause Stata reports over all local toemv = r(N_gt_dot) } if !`tot' di as txt "`bs'{res}no{txt} real changes made" _c else if `tot'==1 di as txt "`bs'{res}1{txt} real change made" _c else di as txt "`bs'{res}`tot'{txt} real changes made" _c if `tomi'==0 { if `extendedF' & `tot' di ", {res}none{txt} to any missing value`bf'" else di "`bf'" } else { if `tot'==`tomi' di "{bf};{sf} {res}all{txt} to missing" _c else di "{bf};{sf} {res}`tomi'{txt} to missing" _c if `extendedF' { if `toemv'==0 di ", none" _c else if `toemv'==`tomi' di ", {res}all{txt} of these" _c else di ", of these {res}`toemv'{txt}" _c di " to {ul:extended}" _c } di "`bf'" } } else { // generate, repeat calls possible local res "{res}" if `brace'==1 local res "" if `asstring'{ tempvar varmiss // misstable does not work on strings, so we do: quietly gen byte `varmiss' = 1 if `var'=="" capture misstable sum `varmiss' //. no qualifiers, cause Stata reports over all local tomi=r(N_lt_dot) if "`tomi'"=="." local tomi 0 // get here if varmiss has no missing local tot `tomi' drop `varmiss' } else { // numeric capture misstable sum `var' //. no qualifiers, cause Stata reports over all local toemv=r(N_gt_dot) if "`toemv'"=="." local toemv 0 local tomi=r(N_eq_dot) if "`tomi'"=="." local tomi 0 local tot = `toemv'+`tomi' } if !`tot' di as txt "`bs'`res'no{txt} missing values generated" _c else if `tot'==1 di as txt "`bs'`res'1{txt} missing value generated" _c else di as txt "`bs'`res'`tot'{txt} missing values generated" _c if `extendedF' { // (strings are not extended) if `toemv'==0 di ", `res'none{txt}" _c else if `toemv'==`tomi' di ", `res'all{txt}" _c else di ", `res'`toemv'{txt}" _c di " of these {ul:extended}" _c } di "`bf'" } end program RRPN, rclass args rpn precedence tempifcond return local RPN `"`rpn'"' // this and following so returns are not destroyed if "`precedence'"!="" return local precedence "`precedence'" if `"`tempifcond'"'!="" return local ifcond `"`tempifcond'"' end program SETT args str set local j = strpos("$validlyDefault","`str':")+3 local k = substr("$validlyDefault",`j',3) local was = "`str':`k'" local now = "`str':`set'" global validlyDefault = subinstr("$validlyDefault","`was'","`now'",1) end program TELL_COND args cond tail local mline : length local cond local char "71" local char2 "76" if `mline'>76 { local char "" local char2 "" } di "{txt} {c TLC}{hline 4}{c BT}{hline `char'}" /// _n `" {c |}{res}`cond' {txt}"' if `tail' di "{txt} {c BLC}{hline 4}{c TT}{hline `char'}" else di " {c BLC}{hline `char2'}" end program TELL_EM, rclass args var in chat genF ifnels whenRAW zvarW * local zvarWW "" local skip 0 if "`whenRAW'"!="" local zvarWW " if `zvarW'" capture misstable sum `var' `in' `zvarWW' * local inplus "" if "`in'"!="" local inplus = ", " + "`in'" local i=r(N_gt_dot) local j=r(N_eq_dot) local k " now" if `genF' local k "" if `i'==0 | `i'==. { if `chat' { di as txt "{c |}" _n "{c LT}{c - } Suppressing the table:" _n /// `"{c |} {res} "Frequency of missing values for `var'`inplus'`whenRAW'"{txt}"' _n /// "{c |} since it would contain no {ul:extended}-missing codes" note:`k' no {ul:extended-missing}-values" /// * " in variable {inp}`var'" _c if `genF' di "{txt}" else di "`inplus'`whenRAW'{txt}" } local skip 1 // so we suppress the table } else { if `chat' { local i = length(`"`var'`inplus'`whenRAW':"')+34 local ii = max(`i',46) local j = max(`ii'-`i'+1,1) local ii = `ii'-12 di as txt "{c |}" _n "{c LT}{c -} Frequency of missing values for {inp}`var'`inplus'`whenRAW'{txt}{space `j'}{c |}" _n /// "{c BLC}{hline 11}{c TT}{hline `ii'}{c BRC}" _c if "`whenRAW'"!="" local zvarWW " & `zvarW'" tab `var' `in' if `var'>=.`zvarWW', m di as txt "{c TLC}{hline 11}{c BT}{hline 35}" } } return local skip `skip' end program TELL_G args mark mark2 mark3 mline local ii "" if _rc==109 local ii " (& non-string)" if _rc!=0 { di as txt "`mark'{bf}global{sf} here must reference a legitimate`ii' expression," /// _n "`mark2'and has only two forms:" /// _n "`mark2'{space 4}{bf}.validly global{sf} {inp}mname {help exp:exp}{txt} {ul:OR} {bf}.validly global{sf} {inp}mname {help exp:if exp}{txt}" /// _n "`mark2'and for options: the first form allows {help validly##extended:{ul:e}xtended}," /// " second allows {help validly##widely:{ul:w}idely}" ULINE `mline' `mark3' } end program TELL_MA args stepf emiss compactN sourceF sourceR everN everIND local sc "{ul:extended}-missing values" if `sourceF' local sc "source-tagged-missing" local opt "" if `compactN' local opt "optimised " local ip ", if possible," local rt "" if `sourceR' { local ip "" local rt "; {help validly##rscode:random tie-breaking}" } di "{text}giving, in local macro{res} {c 'g}fn`stepf''{txt}, the valid `opt'function:" if `everN'|`everIND' { di "{col 7}{c |} (simplifying where variables" _c local opt "" if `everN' { di " lack missing values" _c if `everIND' di ", or" _c } if `everIND' di " are indicator variables" _c di ")" } if `emiss' di "{col 7}{c |} (coded to preserve`ip' `sc'`rt'):" end program TELL_MF args W n di "{space 6}{text}{c |} which in '{bf}m{sf}issing->{bf}F{sf}alse' form " _c if `n'==1 di "(to circumvent Stata's" _n /// _col(7) "{c |} assumption that missing-{it}means{sf}-True) becomes, in {res}{c 'g}fn`W'_mF'{txt}" if `n'==2 di _col(10) "(to coerce Stata's 'assert' " _n /// "{space 6}{text}{c |} to see {ul:missing} as {ul:not}-known-{ul:true}) becomes {res}{c 'g}fn`W'_mF'{txt}" end program TELL_NC args var exp markq di "`markq'but unsuitable for {help validly##global:a conditional call} {hline 2} so {ul:not}:" /// " {inp}.command {err}if \$`var'{sf}{txt}" /// _n `"`markq'{hline 2} for that task, invoke:{space 2}{inp}.vy global `var' {ul:if} `exp'{txt}"' end program TELL_NLR args exp svarF n di di as txt " {inp}exp`n'{txt}{col 7}{c |} " _c if `svarF' di "is the simple variable {c RT} {inp}`exp' {txt}{c LT}" _n `" {hline 5}{c BRC} "' _c else di as txt "contains no logical/relational operators, so" _n /// `" {hline 5}{c RT} we place unmodified {c RT} {inp}`exp' {txt}{c LT} into local macro {res}{c 'g}fn`n''{txt}"' end program TELL_NV args markq di `"`markq'{space 6}(unsuitable for re-use {help validly##ewrap:{ul:within}} {bf}validly{sf})"' end program TELL_PREC args and chat pos markU1 first markU2 remind2a remind2b local times X twice thrice repeatedly * stored as words to allow transfer, move to subscripted for evaluation * if `first' { di as txt "`markU1'{it}reminder{sf}: " _c if `chat' di "following standard syntactical convention" _n `"`markU1'"' _c } if `first'>1 { // to report &| local m = `first'-1 local mm = word(`"`times'"',min(4,`m')) if "`mm'"=="X" local mm "" else local mm " `mm'" di as txt _col(`pos') "logical '{inp}&{txt}' has`mm' {help validly##precedence:taken precedence} over '{inp}|{txt}'" _c if `and' di "; also" else di "" } else { // relation over logical -- explicit report local n 0 foreach i of local remind2a { // put into subscripted vector local ++n local a`n' "`i'" local b`n'=word(`"`remind2b'"',`n') } * forvalues i=1/`n' { if "`a`i''"=="X" continue // has been done local m 1 // instances counter if `i'<`n' { // still to see local jj = `i'+1 forvalues j = `jj'/`n' { if "`a`i''"=="`a`j''" & "`b`i''"=="`b`j''" { // repeat local ++m // count local a`j' X } } } local mm = word(`"`times'"',min(4,`m')) if "`mm'"=="X" local mm "" else local mm " `mm'" if `first' { * di as txt "`markU1'{help validly##precedence:reminder:} " _c local j "" local first 0 } * else di " " _c else { local j `markU2' if `chat' local j `markU1' } di as txt `"`j'"' _col(`pos') `"relation '{inp}`a`i''{txt}' has`mm' {help validly##precedence:taken precedence} over logical '{inp}`b`i''{txt}'"' } } end program TELL_RPN args n di di as txt " {inp}{it}exp{sf}`n'{txt}{col 7}{c |} {help validly##RPN:RPN form}, parsing on logical/relational operators, is:" end program TELL_WS args chat if `chat' { di as txt "{space 6}{text}{c |} {err}Note: {text}since option {bf}widely{sf} has been specified, observations" di "{space 6}{text}{c |}{space 7}will be selected when {c 'g}{bf}fnC{sf}' is true {ul:or indeterminate}" di "{space 6}{c BLC}{hline 71}" } end program TESTS * guidance on overlength. * uses syntax to decode call: N =`condexp' capture syntax name =/exp //sensitive to overlength exps local i = _rc if "`namelist'"=="X" local namelist "" if `i'==130 { di _n "{err}The constructed function {c 'g}{bf}fn`namelist'{sf}' is too complex for Stata;{txt}" COMPACT "`2'" 0 "err" di "{help validly##toolong:{bf}click here{sf} for suggestions} on how how to circumvent" _n /// "the error: " _c error 130 } end program TLABEL , rclass args n colon value if "`value'"==":" { di as err "value label missing after colon" error 111 } if `n' exit if "`colon'"!=":" error 198 capture label list `value' * if _rc!=0 label list `value' return local tlabel "" if _rc!=0 return local tlabel `value' end program TOGSET args str local alphaLC "abcdefghijklmnopqrstuvwxyz" * to set specified toggle if "`str'"=="ng" { local error 0 if length("`2'")!=2 local error 1 if substr("`2'",1,1)!="." local error 1 if indexnot(substr("`2'",2,1),"`alphaLC'")!=0 local error 1 if `error' { di as err _n `"'`2'' where {ul:extended}-missing value expected"' error 7 } SETT `str' "`2' " // to keep 3 char spacing } else if "`str'"=="t2" { if `2'<0 | `2'>9 error 198 SETT `str' "`2' " // to keep 3 char spacing } else { if "`2'"=="OFF" | "`2'"=="off" SETT "`str'" "OFF" else if "`2'"=="ON" | "`2'"=="on" SETT `str' "ON " else { di as txt "Unrecognised parameter '{inp}`2'{txt}', whence " _c error 199 } } end program ULINE args mline mark3 local j = `mark3'-3 local k = `mline'-`j'-1 di as txt "{space `j'}{c BLC}{hline `k'}" end program ULINE2 di as txt "{col 7}{c BLC}{hline 71}" end program VCHECK args asstring var * check existing variable OK for type if `asstring' { capture confirm string variable `var', ex if _rc!=0 { if _rc==7 di as err " Numeric variable " _c confirm string variable `var', ex // error exit } } else { capture confirm numeric variable `var', ex if _rc!=0 { if _rc==7 di as err " String variable " _c confirm numeric variable `var', ex // error exit } } end program WARN_MI args warn_mi chat mark local tell "a conditional test-for-missing" if `warn_mi' local tell "a conditional mention of {inp}mi(){txt}" if `chat' { * LIN 3 // need to tag this ## di "{c |}{col 15}{err}{bf}warning{sf}{txt} {c +} {bf}validly{sf} noticed `tell', " LIN 2 _c di "but has not analysed its use; {help validly##reminder:click here}" LIN 2 _c di "for discussion of why that use might matter. " } else di "`mark'{err}{bf}warning{sf}:{txt} on `tell', {help validly##reminder:click here for discussion}" end * note on preferred editor colours: font Courier New, 9pt * built-in B, B comments DG, - compound M,B key-words DB, B * Macros P, B numbers B, B opeartors R, B strings M, -