*! version 1.6 *! Doug Hemken *! 4 Feb 2019 // ISSUES // ====== // put preamble(s) after any initial dynamic tags, like dd_version or dd_include // better, more extensive preamble, e.g. linesize, other options? // NOGRaph option // capture program drop stmd2dyn // capture mata: mata clear program define stmd2dyn, rclass syntax anything(name=infile), [ /// SAVing(string) replace /// noGRAPHlinks /// ] version 15 local infile = ustrtrim(usubinstr(`"`infile'"', `"""', "", .)) *display `"infile is `infile'"' confirm file `"`infile'"' *display "infile confirmed" if ("`saving'" == "" ) { mata:(void)pathchangesuffix("`infile'", "dyn", "saving", 0) mata: (void)st_local("saving", pathjoin("`c(pwd)'", `"`saving'"')) } mata: (void)pathresolve("`c(pwd)'", `"`saving'"', "saving") *display `"saving `saving'"' local issame = 0 mata: (void)filesarethesame("`infile'", "`saving'", "issame") if ("`issame'" == "1") { display in error "target file can not be the same as the source file" exit 602 } if ("`replace'"=="") { confirm new file "`saving'" } * Read in file mata: X=docread("`infile'") //mata: X * Then identify code blocks and tags mata: fenceinfo = _fence_info(X) // fences //mata: fenceinfo mata: infotags = _info_tags(X) // retrieve infotags mata: tagmatchs = _tag_match(infotags) // parse infotags mata: dotags = _dd_do(fenceinfo, tagmatchs) // generate <> * Identify display directives mata: X = _inline_code(X, fenceinfo[.,3]) //mata: X * assemble pieces of a dyndoc mata: document = _stitch(X, fenceinfo, dotags) //mata: document * Write out the result mata: saving = st_local("saving") mata: docwrite(saving, document) display " {text:Output saved as {it:`saving'}}" * Finish up return local outfile "`saving'" end mata string colvector docread(string scalar filename) { fh = fopen(filename, "r") string colvector document document= J(0,1,"") while ((line=fget(fh))!=J(0,0,"")) { document = (document\line) } fclose(fh) return(document) } void function docwrite(string scalar filename, /// string colvector document) { unlink(filename) fh = fopen(filename, "w") for (i=1; i<=length(document); i++) { fput(fh, document[i]) } fclose(fh) } real matrix function _fence_info(string colvector X) { codefence = "^( ? ? ?)(```+|~~~+)([ ]*)$" infofence = "^( ? ? ?)(```+|~~~+)\{?(s|stata)\/?(,.*)?\}?$" fence = ustrregexm(X, codefence) codebegin = ustrregexm(X, infofence) fence = fence + codebegin // any code fence prespace = J(rows(X),1,.) // possible spaces before code fence cbdepth = J(rows(X),1,0) // depth of code fencing fencel = J(rows(X),7,.) // # of characters used in fence doblock = 0 // dynamic code for (i=1; i<=rows(X); i++) { // find and characterize fences if (i>=2) { //lag fence depth and fence length(s) cbdepth[i]=cbdepth[i-1] fencel[i,.]=fencel[i-1,.] } if (ustrregexm(X[i,1], infofence) | ustrregexm(X[i,1], codefence)) { if (ustrregexm(X[i,1], infofence)) { prespace[i] = ustrlen(ustrregexs(1)) fl = ustrlen(ustrregexs(2)) } else if (ustrregexm(X[i,1], codefence)) { prespace[i] = ustrlen(ustrregexs(1)) fl = ustrlen(ustrregexs(2)) } // doable? if (cbdepth[i]==0 & ustrregexm(X[i,1], infofence)) { cbdepth[i]=cbdepth[i]+1 fencel[i, cbdepth[i]] = fl codebegin[i] = 1 // redundant doblock = 1 } // not doable: deeper? else if (ustrregexm(X[i,1], infofence) | cbdepth[i]==0) { cbdepth[i]=cbdepth[i]+1 fencel[i, cbdepth[i]] = fl codebegin[i] = 0 // redundant } // also deeper else if (fl < fencel[i, cbdepth[i]]) { cbdepth[i]=cbdepth[i]+1 fencel[i, cbdepth[i]] = fl } else { fencel[i, cbdepth[i]] = . cbdepth[i] = cbdepth[i]-1 } // is this the end of doable? if (cbdepth[i]==0 & doblock) { codebegin[i] = -1 doblock=0 } } } return(fence,codebegin,cbdepth,prespace,fencel) } string colvector function _info_tags(string colvector X) { infofence = "^( ? ? ?)(```+|~~~+)\{?(s|stata)\/?(,.*)?\}?$" //infofence infotags = J(rows(X),1,"") for (i=1; i<=rows(X); i++) { if (ustrregexm(X[i,1], infofence)) { infotags[i] = ustrregexs(4) } } return(infotags) } real matrix function _tag_match(string colvector infotags) { codeopts = ustrregexm(infotags, ",") noeval = ustrregexm(infotags, "eval=FALSE") noecho1 = ustrregexm(infotags, "echo=FALSE") noecho2 = ustrregexm(infotags, "\/") noecho3 = ustrregexm(infotags, "nocommands") noecho4 = ustrregexm(infotags, "quietly") noecho = noecho1+noecho2+noecho3+noecho4 noresults1 = ustrregexm(infotags, "results=FALSE") noresults2 = ustrregexm(infotags, `"results="hide""') noresults3 = ustrregexm(infotags, "nooutput") noresults4 = ustrregexm(infotags, "quietly") noresults = noresults1+noresults2+noresults3+noresults4 //noprompt1 = ustrregexm(infotags, "noprompt=TRUE") //noprompt2 = ustrregexm(infotags, "noprompt") //noprompt = noprompt1+noprompt2 noprompt = ustrregexm(infotags, "noprompt") return(codeopts, noeval, noecho, noresults, noprompt) } string colvector function _dd_do(real matrix fenceinfo, real matrix tagmatch) { dotags = J(rows(fenceinfo),1,"") noeval = 0 for (i=1; i<=rows(fenceinfo); i++) { if (fenceinfo[i,2]==1) { if (sum(tagmatch[i,.])==0) dotags[i,1]=("<>") else if (tagmatch[i,3]==1) { if (tagmatch[i,4]==0) dotags[i,1]=("<>") else if (tagmatch[i,4]==1) dotags[i,1]=("<>") } else if (tagmatch[i,3]==0 & tagmatch[i,4]==1) dotags[i,1]=("<>") else if (tagmatch[i,5]==1) dotags[i,1]=("<>") else if (tagmatch[i,2]==1) noeval=1 } else if (fenceinfo[i,2]==-1) { if (noeval==0) dotags[i,1]=("<>") else if (noeval==1) noeval=0 } else dotags[i,1]=("") } return(dotags) } string colvector function _gr_preamble() { GR = "<>"\ "capture graph describe Graph"\ "tempname gdate"\ `"local \`gdate' = "\`r(command_date)' \`r(command_time)'" "'\ "<>" return(GR) } string colvector function _gr_link() { GL = `"<>"' \ `"capture _return hold rtemp"' \ `"capture graph describe Graph"' \ `"local checkdate = "\`r(command_date)' \`r(command_time)'" "' \ `"<>"' \ `"<>"' \ `"<>"' \ `"<>"' \ `"<>"' \ `"local \`gdate' = "\`r(command_date)' \`r(command_time)'""' \ `"capture _return restore rtemp"' \ `"sleep 500"' \ `"<>"' return(GL) } string colvector function _stitch(string colvector X, real matrix fenceinfo, string colvector dotags) { lce = 0 // last code end line quiet = 0 // quietly flag Y = _gr_preamble() for (i=1; i<=rows(X); i++) { //X[i,.] if (fenceinfo[i,2]==1) { if (dotags[i,1]=="<>") { //X[i,.],dotags[i,.] Y = Y \ X[(lce+1)..(i-1),.]\dotags[i,.] lce = i quiet = 1 } else { Y = Y \ X[(lce+1)..i,.]\dotags[i,.] lce = i } } else if (fenceinfo[i,2]==-1) { if (quiet==1) { Y= Y \X[(lce+1)..(i-1),.]\dotags[i,.]\_gr_link() lce = i quiet=0 } else { Y= Y \X[(lce+1)..(i-1),.]\dotags[i,.]\X[i,.]\_gr_link() lce = i } } else if (i==rows(X)) { Y= Y \ X[(lce+1)..i,.] } } return(Y) } string colvector function _inline_code(string colvector X, real colvector cbdepth) { for (i=1; i<=rows(X); i++) { if (cbdepth[i] == 0) { dispdir = ustrregexm(X[i,1], "(`|~)\{?(s|stata)\}?( )+(.*)(`)") while (dispdir) { X[i,1] = ustrregexra(X[i,1], "(`|~)\{?(s|stata)\}?( )+", "<>") dispdir = ustrregexm(X[i,1], "(`|~)\{?(s|stata)\}?( )+(.*)(`)") } } } return(X) } end