diff options
author | Karl Berry <karl@freefriends.org> | 2023-09-09 20:09:19 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2023-09-09 20:09:19 +0000 |
commit | 0d22e428f9fc7235d13110f382e68b34d03b4294 (patch) | |
tree | 2f93e2aa6bd0de81da0619558149e5cc45e6ad0f /Master/texmf-dist/tex/latex/runcode | |
parent | 4ca20c86f05ea21a8f43cd4e3cea6028a7b472dc (diff) |
runcode (9sep23)
git-svn-id: svn://tug.org/texlive/trunk@68223 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/latex/runcode')
-rw-r--r-- | Master/texmf-dist/tex/latex/runcode/runcode.sty | 322 |
1 files changed, 181 insertions, 141 deletions
diff --git a/Master/texmf-dist/tex/latex/runcode/runcode.sty b/Master/texmf-dist/tex/latex/runcode/runcode.sty index 1fe5b079568..06c3d346e05 100644 --- a/Master/texmf-dist/tex/latex/runcode/runcode.sty +++ b/Master/texmf-dist/tex/latex/runcode/runcode.sty @@ -4,7 +4,9 @@ % % This package is based on an ongoing work by Haim Bar and HaiYing Wang, and comments and questions are welcome! -\ProvidesPackage{runcode}[2023/06/23 runcode v2.0] +\ProvidesPackage{runcode}[2023/09/08 runcode v2.2] + +\def \langs {} \newif\ifruncode % Change to \runcodefalse if you want to suspend code execution @@ -48,105 +50,73 @@ \notnohuptrue \DeclareOption{nohup}{\notnohupfalse} +\newcounter{portNo} +\setcounter{portNo}{65430} + +\NewDocumentCommand{\InitLang}{m}{ + % Create a configuration file for the server for the language provided + % in the argument, if it does not exist, and add to the serverslist.txt file. + \IfFileExists{#1.config}{}{ + \newwrite\tempfile + \immediate\openout\tempfile=#1.config + \immediate\write\tempfile{[SERVERCONFIG]} + \immediate\write\tempfile{PORT = \theportNo} + \immediate\write\tempfile{DEBUGFILE = #1debug.txt} + \immediate\write\tempfile{PIPETIMEOUT = 60} + \immediate\closeout\tempfile + \edef\langs{\langs#1_} + \stepcounter{portNo} + } + % Start the server. Need to run just once. Can comment out after the first + % compilation, but remember to terminate the server + \ifnotnohup + {\immediate\write18{python3 -c 'from talk2stat.talk2stat import server,client; server("./","#1") if not client("./","#1","``` ```") else print("server is already running")'}} + \else + {\immediate\write18{nohup python3 -c 'from talk2stat.talk2stat import server,client; server("./","#1") if not client("./","#1","``` ```") else print("server is already running")' &}} + \fi +} + \DeclareOption{R}{ - % create a configuration file for R server if it does not exist. - \IfFileExists{R.config}{}{ - \newwrite\tempfile - \immediate\openout\tempfile=R.config - \immediate\write\tempfile{[SERVERCONFIG]} - \immediate\write\tempfile{PORT = 65432} - \immediate\write\tempfile{DEBUGFILE = Rdebug.txt} - \immediate\write\tempfile{PIPETIMEOUT = 60} - \immediate\closeout\tempfile - } - % Start the server. Need to run just once. Can comment out after the first - % compilation, but remember to terminate the server - \ifnotnohup - {\immediate\write18{python3 -c 'from talk2stat.talk2stat import server,client; server("./","R") if not client("./","R","``` ```") else print("server is already running")'}} - \else - {\immediate\write18{nohup python3 -c 'from talk2stat.talk2stat import server,client; server("./","R") if not client("./","R","``` ```") else print("server is already running")' &}} - \fi + \InitLang{R} } \DeclareOption{julia}{ - % create a configuration file for R server if it does not exist. - \IfFileExists{julia.config}{}{ - \newwrite\tempfile - \immediate\openout\tempfile=julia.config - \immediate\write\tempfile{[SERVERCONFIG]} - \immediate\write\tempfile{PORT = 65431} - \immediate\write\tempfile{DEBUGFILE = juliadebug.txt} - \immediate\write\tempfile{PIPETIMEOUT = 60} - \immediate\closeout\tempfile - } - % Start the server. Need to run just once. Can comment out after the first - % compilation, but remember to terminate the server - \ifnotnohup - {\immediate\write18{python3 -c 'from talk2stat.talk2stat import server,client; server("./","julia") if not client("./","julia","``` ```") else print("server is already running")'}} - \else - {\immediate\write18{nohup python3 -c 'from talk2stat.talk2stat import server,client; server("./","julia") if not client("./","julia","``` ```") else print("server is already running")' &}} - \fi + \InitLang{julia} } \DeclareOption{matlab}{ - % create a configuration file for R server if it does not exist. - \IfFileExists{matlab.config}{}{ - \newwrite\tempfile - \immediate\openout\tempfile=matlab.config - \immediate\write\tempfile{[SERVERCONFIG]} - \immediate\write\tempfile{PORT = 65430} - \immediate\write\tempfile{DEBUGFILE = matlabdebug.txt} - \immediate\write\tempfile{PIPETIMEOUT = 60} - \immediate\closeout\tempfile - } - % Start the server. Need to run just once. Can comment out after the first - % compilation, but remember to terminate the server - \ifnotnohup - {\immediate\write18{python3 -c 'from talk2stat.talk2stat import server,client; server("./","matlab") if not client("./","matlab","``` ```") else print("server is already running")'}} - \else - {\immediate\write18{nohup python3 -c 'from talk2stat.talk2stat import server,client; server("./","matlab") if not client("./","matlab","``` ```") else print("server is already running")' &}} - \fi + \InitLang{matlab} } \DeclareOption{python}{ - % create a configuration file for Python server if it does not exist. - \IfFileExists{python.config}{}{ - \newwrite\tempfile - \immediate\openout\tempfile=python.config - \immediate\write\tempfile{[SERVERCONFIG]} - \immediate\write\tempfile{PORT = 65433} - \immediate\write\tempfile{DEBUGFILE = pythondebug.txt} - \immediate\write\tempfile{PIPETIMEOUT = 60} - \immediate\closeout\tempfile - } - % Start the server. Need to run just once. Can comment out after the first - % compilation, but remember to terminate the server - \ifnotnohup - {\immediate\write18{python3 -c 'from talk2stat.talk2stat import server,client; server("./","python") if not client("./","python","``` ```") else print("server is already running")'}} - \else - {\immediate\write18{nohup python3 -c 'from talk2stat.talk2stat import server,client; server("./","python") if not client("./","python","``` ```") else print("server is already running")' &}} - \fi + \InitLang{python} } + \DeclareOption{stopserver}{ \AtEndDocument{ - %% stop the server when the pdf compilation is done. - \IfFileExists{R.config}{ - \immediate\write18{python3 -c 'from talk2stat.talk2stat import client; client("./","R","QUIT")'} - }{} - \IfFileExists{julia.config}{ - \immediate\write18{python3 -c 'from talk2stat.talk2stat import client; client("./","julia","QUIT")'} - }{} - \IfFileExists{matlab.config}{ - \immediate\write18{python3 -c 'from talk2stat.talk2stat import client; client("./","matlab","QUIT")'} - }{} - \IfFileExists{python.config}{ - \immediate\write18{python3 -c 'from talk2stat.talk2stat import client; client("./","python","QUIT")'} + \IfFileExists{serverslist.txt}{ + \newread\file + \openin\file=serverslist.txt + \immediate\readline\file to\lang + \closein\file + \StrCount{\lang}{\detokenize{_}}[\langNo] + \newcounter{x} + \forloop{x}{0}{\value{x} < \langNo}{ + \StrBefore{\lang}{\detokenize{_}}[\curlang] + \StrBehind{\lang}{\detokenize{_}}[\lang] + %% stop the server when the pdf compilation is done. + \IfFileExists{\curlang.config}{ + \immediate\write18{python3 -c 'from talk2stat.talk2stat import client; client("./","\curlang","QUIT")'} + }{} + } }{} -}} + } +} \ProcessOptions* +\usepackage{etoolbox} \usepackage{morewrites} % allow more than 16 \write \usepackage[many]{tcolorbox} % to put boxes around text \tcbset{colframe=blue!25,colback=blue!10} % default colors for box output @@ -157,6 +127,15 @@ \usepackage{xifthen} \usepackage{xparse} \usepackage{xstring} +\usepackage{forloop} + +%%%% check if \langs is empty !! +\ifdefempty{\langs}{}{ + \newwrite\outfile + \immediate\openout\outfile=serverslist.txt + \immediate\write\outfile{\langs} + \immediate\closeout\outfile} +{} % Use minted by default; but the user can change to listings or fvextra. \ifminted @@ -205,7 +184,6 @@ \setvalue{\tmpname}{} % initialize with a blank - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % \showCode prints the source code, using minted or listings for a pretty layout % Arg #1 is the programming language, @@ -320,8 +298,63 @@ } +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \writeChunk writes a selected chunk from source code as a .txt file. +% Arg #1 is the source file name, +% Arg #2 is the chunk identifier +% Args #3 and #4 are the beginning and end markings of a chunk (optional). + +\NewDocumentCommand{\writeChunk}{m m O{label===} O{===end}} { + \IfFileExists{#1}{ + \endlinechar=-1 + \def\printcode{0} % don't print until we see the begin-block string + \newread\file + \openin\file=#1 + \newwrite\outfile + \immediate\openout\outfile=\generated/#1-#2.txt + \loop\unless\ifeof\file + \immediate\readline\file to\fileline + \ifnum\printcode=1 + \IfSubStr{\fileline}{\detokenize{#4}}{ + \renewcommand{\printcode}{-1}}{\immediate\write\outfile{\fileline}} + \fi + \IfSubStr{\fileline}{\detokenize{#3}}{ + \StrBehind{\fileline}{\detokenize{#3}}[\chunkname] + \IfStrEq*{\detokenize{#2}}{\chunkname}{\renewcommand{\printcode}{1}}{} + } + {} + \repeat + \closein\file + \immediate\closeout\outfile + \endlinechar=13 + \ifnum\printcode=0 + \textcolor{red}{\textbf{Label #2 not found in #1}} + \fi + }{\textcolor{red}{\textbf{Source file #1 not found}}} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \showChunk prints a selected chunk from source code, using minted or listings for a pretty layout. +% The chunk is identified in the source code by two strings that define the beginning and end of the chunk. +% The default beginning is label===<name> where <name> should be a unique user-defined chunk ID. +% The default end marker is ===end +% In the code, these markers should appear after a comment character, so that the code will run. +% Arg #1 is the programming language, +% Arg #2 is the source file name, +% Args #3 is the chunk identifier +% Args #4 and #5 are the beginning and end markings of a chunk (optional). + +\NewDocumentCommand{\showChunk}{m m m O{label===} O{===end}}{ + \IfFileExists{#2}{ + \writeChunk{#2}{#3}[#4][#5] + \IfFileExists{\generated/#2-#3.txt}{\showCode{#1}{\generated/#2-#3.txt}} + {\textcolor{red}{\textbf{Label #3 not found in #2}}} + }{\textcolor{red}{\textbf{Source file #2 not found}}} +} + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% \inln{Arg #1}{Arg #2}[Arg #3] is used to execute short source code +% \inln{Arg #1}{Arg #2}[Arg #3][Arg #4] is used to execute short source code % and embed resulting output. % Arg #1 is the executable program; % Arg #2 is the source code; @@ -342,7 +375,7 @@ \IfBeginWith{#2}{```}{\ifruncode\immediate\write18{#1 > \tmpname.tex}\unskip\fi} {\newwrite\tempfile \immediate\openout\tempfile=\tmpname.txt - \immediate\write\tempfile{#2} + \immediate\write\tempfile{\detokenize{#2}} \immediate\closeout\tempfile \ifruncode \immediate\write18{#1 \tmpname.txt > \tmpname.tex}\unskip @@ -367,7 +400,7 @@ {\textcolor{red}{\textbf{Output file \tmpname~ not found}}}} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% Extended and language-specific comments: +% Extended and language-specific commands: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % \runCodeIncOut: runs an external code and embeds the output. @@ -388,77 +421,84 @@ \includeOutput{#4}[#5] } +\newcommand{\runcmd}[2] +{python3 -c 'from talk2stat.talk2stat import client; client("./","#1",#2)'} + % R -\NewDocumentCommand{\runR}{O{python3 -c 'from talk2stat.talk2stat import client; client("./","R","#2")'} m m O{}} -{ - \def\runcmd{python3 -c 'from talk2stat.talk2stat import client; client("./","R","#2")'} - \ifstrequal{#1}{\runcmd} - {\runExtCode{#1}{}{#3}[#4]} +% It will be used as a template for other languages +\newcommand{\LANG}{R} +\newcommand{\LANGcmd}{R} + +\expandafter\NewDocumentCommand\csname run\LANG\endcsname +{O{} m m O{}} +{\ifthenelse{\isempty{#1}} + {\runExtCode{\runcmd{\LANGcmd}{"#2"}}{#2}{#3}[#4]} {\runExtCode{#1}{#2}{#3}[#4]} } -\NewDocumentCommand{\runRIncOut} -{O{python3 -c 'from talk2stat.talk2stat import client; client("./","R","#2")'} m O{} O{} O{vbox}} -{\def\runcmd{python3 -c 'from talk2stat.talk2stat import client; client("./","R","#2")'} - \ifstrequal{#1}{\runcmd} - {\runCodeIncOut{#1}{}[#3][#4][#5]} - {\runCodeIncOut{#1}{#2}[#3][#4][#5]}} +\expandafter\NewDocumentCommand\csname run\LANG IncOut\endcsname +{O{} m O{} O{} O{vbox}} +{\ifthenelse{\isempty{#1}} + {\runCodeIncOut{\runcmd{\LANGcmd}{"#2"}}{#2}[#3][#4][#5]} + {\runCodeIncOut{#1}{#2}[#3][#4][#5]} +} -\NewDocumentCommand{\inlnR}{O{python3 -c 'from talk2stat.talk2stat import client; client("./","R","\tmpname.txt")'} m O{} O{inline}}{\IfBeginWith{#2}{```}{\inln{python3 -c 'from talk2stat.talk2stat import client;rc0="""#2""";rc0=rc0.lstrip("```").rstrip("```");client("./","R",f"```{rc0}```")'}{#2}[#3][#4]}{\inln{#1}{#2}[#3][#4]}} +\expandafter\NewDocumentCommand\csname inln\LANG\endcsname +{O{} m O{} O{inline}} +{\ifthenelse{\isempty{#1}} + {\IfBeginWith{#2}{```} + {\inln{\runcmd{\LANGcmd}{r"""\detokenize{#2}"""}}{#2}[#3][#4]} + {\inln{\runcmd{\LANGcmd}{"\tmpname.txt"}}{#2}[#3][#4]} + } + {\inln{#1}{#2}[#3][#4]} +} -% Julia -\NewDocumentCommand{\runJulia}{O{python3 -c 'from talk2stat.talk2stat import client; client("./","julia","#2")'} m m O{}} -{ - \def\runcmd{python3 -c 'from talk2stat.talk2stat import client; client("./","julia","#2")'} - \ifstrequal{#1}{\runcmd} - {\runExtCode{#1}{}{#3}[#4]} - {\runExtCode{#1}{#2}{#3}[#4]} +\expandafter\NewDocumentCommand\csname run\LANG Chunk\endcsname +{O{} m m O{} O{} O{vbox}} +{\IfFileExists{\generated/#2-#3.txt}{}{\writeChunk{#2}{#3}} + {\csname run\LANG IncOut\endcsname[#1]{\generated/#2-#3.txt}[#4][#2-#3][#6]} } -\NewDocumentCommand{\runJuliaIncOut} -{O{python3 -c 'from talk2stat.talk2stat import client; client("./","julia","#2")'} m O{} O{} O{vbox}} -{\def\runcmd{python3 -c 'from talk2stat.talk2stat import client; client("./","julia","#2")'} - \ifstrequal{#1}{\runcmd} - {\runCodeIncOut{#1}{}[#3][#4][#5]} - {\runCodeIncOut{#1}{#2}[#3][#4][#5]}} +% Julia +\NewDocumentCommand{\runJulia}{O{} m m O{}} +{\renewcommand{\LANGcmd}{julia} \runR[#1]{#2}{#3}[#4]} + +\NewDocumentCommand{\runJuliaIncOut}{O{} m O{} O{} O{vbox}} +{\renewcommand{\LANGcmd}{julia} \runRIncOut[#1]{#2}[#3][#4][#5]} + +\NewDocumentCommand{\inlnJulia}{O{} m O{} O{inline}} +{\renewcommand{\LANGcmd}{julia} \inlnR[#1]{#2}[#3][#4]} + +\NewDocumentCommand{\runJuliaChunk}{O{} m m O{} O{} O{vbox}} +{\renewcommand{\LANGcmd}{julia} \runRChunk[#1]{#2}{#3}[#4][#5][#6]} -\NewDocumentCommand{\inlnJulia}{O{python3 -c 'from talk2stat.talk2stat import client; client("./","julia","\tmpname.txt")'} m O{} O{inline}}{\IfBeginWith{#2}{```}{\inln{python3 -c 'from talk2stat.talk2stat import client;rc0="""#2""";rc0=rc0.lstrip("```").rstrip("```");client("./","julia",f"```{rc0}```")'}{#2}[#3][#4]}{\inln{#1}{#2}[#3][#4]}} % Matlab -\NewDocumentCommand{\runMatlab}{O{python3 -c 'from talk2stat.talk2stat import client; client("./","matlab","#2")'} m m O{}} -{ - \def\runcmd{python3 -c 'from talk2stat.talk2stat import client; client("./","matlab","#2")'} - \ifstrequal{#1}{\runcmd} - {\runExtCode{#1}{}{#3}[#4]} - {\runExtCode{#1}{#2}{#3}[#4]} -} +\NewDocumentCommand{\runMatLab}{O{} m m O{}} +{\renewcommand{\LANGcmd}{matlab} \runR[#1]{#2}{#3}[#4]} + +\NewDocumentCommand{\runMatLabIncOut}{O{} m O{} O{} O{vbox}} +{\renewcommand{\LANGcmd}{matlab} \runRIncOut[#1]{#2}[#3][#4][#5]} + +\NewDocumentCommand{\inlnMatLab}{O{} m O{} O{inline}} +{\renewcommand{\LANGcmd}{matlab} \inlnR[#1]{#2}[#3][#4]} -\NewDocumentCommand{\runMatlabIncOut} -{O{python3 -c 'from talk2stat.talk2stat import client; client("./","matlab","#2")'} m O{} O{} O{vbox}} -{\def\runcmd{python3 -c 'from talk2stat.talk2stat import client; client("./","matlab","#2")'} - \ifstrequal{#1}{\runcmd} - {\runCodeIncOut{#1}{}[#3][#4][#5]} - {\runCodeIncOut{#1}{#2}[#3][#4][#5]}} +\NewDocumentCommand{\runMatLabChunk}{O{} m m O{} O{} O{vbox}} +{\renewcommand{\LANGcmd}{matlab} \runRChunk[#1]{#2}{#3}[#4][#5][#6]} -\NewDocumentCommand{\inlnMatlab}{O{python3 -c 'from talk2stat.talk2stat import client; client("./","matlab","\tmpname.txt")'} m O{} O{inline}}{\IfBeginWith{#2}{```}{\inln{python3 -c 'from talk2stat.talk2stat import client;rc0="""#2""";rc0=rc0.lstrip("```").rstrip("```");client("./","matlab",f"```{rc0}```")'}{#2}[#3][#4]}{\inln{#1}{#2}[#3][#4]}} % Python -\NewDocumentCommand{\runPython}{O{python3 -c 'from talk2stat.talk2stat import client; client("./","python","#2")'} m m O{}} -{ - \def\runcmd{python3 -c 'from talk2stat.talk2stat import client; client("./","python","#2")'} - \ifstrequal{#1}{\runcmd} - {\runExtCode{#1}{}{#3}[#4]} - {\runExtCode{#1}{#2}{#3}[#4]} -} +\NewDocumentCommand{\runPython}{O{} m m O{}} +{\renewcommand{\LANGcmd}{python} \runR[#1]{#2}{#3}[#4]} + +\NewDocumentCommand{\runPythonIncOut}{O{} m O{} O{} O{vbox}} +{\renewcommand{\LANGcmd}{python} \runRIncOut[#1]{#2}[#3][#4][#5]} -\NewDocumentCommand{\runPythonIncOut} -{O{python3 -c 'from talk2stat.talk2stat import client; client("./","python","#2")'} m O{} O{} O{vbox}} -{\def\runcmd{python3 -c 'from talk2stat.talk2stat import client; client("./","python","#2")'} - \ifstrequal{#1}{\runcmd} - {\runCodeIncOut{#1}{}[#3][#4][#5]} - {\runCodeIncOut{#1}{#2}[#3][#4][#5]}} +\NewDocumentCommand{\inlnPython}{O{} m O{} O{inline}} +{\renewcommand{\LANGcmd}{python} \inlnR[#1]{#2}[#3][#4]} -\NewDocumentCommand{\inlnPython}{O{python3 -c 'from talk2stat.talk2stat import client; client("./","python","\tmpname.txt")'} m O{} O{inline}}{\IfBeginWith{#2}{```}{\inln{python3 -c 'from talk2stat.talk2stat import client;rc0="""#2""";rc0=rc0.lstrip("```").rstrip("```");client("./","python",f"```{rc0}```")'}{#2}[#3][#4]}{\inln{#1}{#2}[#3][#4]}} +\NewDocumentCommand{\runPythonChunk}{O{} m m O{} O{} O{vbox}} +{\renewcommand{\LANGcmd}{python} \runRChunk[#1]{#2}{#3}[#4][#5][#6]} %%%%%%%% @@ -503,4 +543,4 @@ } -\endinput
\ No newline at end of file +\endinput |