From 91d1b2e7a51c9ba060bb45fbfe35bd369f6f40c5 Mon Sep 17 00:00:00 2001 From: Takahiro Ueda Date: Sat, 1 Nov 2025 14:41:00 +0900 Subject: [PATCH] feat: implement #continuedo --- check/features.frm | 86 +++++++++++++++++++++++++++++++++++++++++-- doc/manual/prepro.tex | 22 +++++++++++ sources/pre.c | 68 +++++++++++++++++++++++++++++++--- 3 files changed, 167 insertions(+), 9 deletions(-) diff --git a/check/features.frm b/check/features.frm index 7394f5ae..95cbf5ed 100644 --- a/check/features.frm +++ b/check/features.frm @@ -2390,11 +2390,91 @@ assert succeeded? assert result("F") =~ expr("f(1,2,4,5)") *--#] Issue243_1 : *--#[ Issue243_2 : -#continuedo +NF f; +L F = 1; +#define i "9" +#do i=1,2 + #do i=1,3 + #do i=1,8 + #if `i' == 3 + #continuedo 2 + #endif + multiply right, f(`i'); + #enddo + multiply right, f(-`i'); + #enddo + multiply right, f(-`i'); +#enddo +multiply right, f(`i'); +chainin f; +P; .end -assert preprocess_error? -assert stdout =~ exact_pattern("#continuedo without #do") +assert succeeded? +assert result("F") =~ expr("f(1,2,1,2,1,2,-1,1,2,1,2,1,2,-2,9)") *--#] Issue243_2 : +*--#[ Issue243_3 : +NF f; +L F = 1; +#do i=1,3 + #do i=1,2 + #continuedo 0 + multiply right, f(`i'); + #enddo +#enddo +chainin f; +P; +.end +assert succeeded? +assert result("F") =~ expr("f(1,2,1,2,1,2)") +*--#] Issue243_3 : +*--#[ Issue243_e1 : +#continuedo +.end +assert preprocess_error?("#continuedo without #do") +*--#] Issue243_e1 : +*--#[ Issue243_e2 : +#do i=1,3 + #continuedo -1 +#enddo +.end +assert preprocess_error?("Improper syntax of #continuedo instruction") +*--#] Issue243_e2 : +*--#[ Issue243_e3 : +#do i=1,3 + #continuedo 1a +#enddo +.end +assert preprocess_error?("Improper syntax of #continuedo instruction") +*--#] Issue243_e3 : +*--#[ Issue243_e4 : +#do i=1,3 + #continuedo 2 +#enddo +.end +assert preprocess_error?("Too many loop levels requested in #continuedo instruction") +*--#] Issue243_e4 : +*--#[ Issue243_e5 : +#procedure foo + #continuedo +#endprocedure +#do i=1,3 + #call foo +#enddo +.end +assert preprocess_error?("Trying to jump out of a procedure with a #continuedo instruction") +*--#] Issue243_e5 : +*--#[ Issue243_e6 : +#procedure foo + #continuedo 2 +#endprocedure +#do i=1,3 + #do j=1,3 + #call foo + #enddo +#enddo +.end +assert preprocess_error?("Trying to jump out of a procedure with a #continuedo instruction") +*--#] Issue243_e6 : *--#[ Issue392_ContinuationLines_1 : #: ContinuationLines 1 * Setting ContinuationLines to 0 should remove continuation line limit. diff --git a/doc/manual/prepro.tex b/doc/manual/prepro.tex index f417757e..deb63fb2 100644 --- a/doc/manual/prepro.tex +++ b/doc/manual/prepro.tex @@ -632,6 +632,28 @@ \section{\#commentchar} The default commentary character is $\ast$. %--#] commentchar : +%--#[ continuedo : + +\section{\#continuedo} +\label{precontinuedo} + +\noindent Syntax: + +\#continuedo [{\tt<}number{\tt>}] + +\noindent See also \#do (\ref{predo}) and \#enddo (\ref{preenddo}) + +\noindent The \#continuedo\index{\#continuedo} instruction allows one to +jump to the next iteration of a \#do loop. +If a (nonzero integer) number is specified +it indicates the number of enclosing loops +that the program should continue to the next iteration of. +Control will continue at the beginning of the next iteration +of the enclosing loop indicated by `number'. +The default value is one. +If the value is zero the instruction has no effect. + +%--#] continuedo : %--#[ create : \section{\#create} diff --git a/sources/pre.c b/sources/pre.c index d51862b5..524cc834 100644 --- a/sources/pre.c +++ b/sources/pre.c @@ -32,6 +32,7 @@ #[ Includes : */ #include "form3.h" +#include "comtool.h" #ifdef WITHFLOAT #include "math.h" #endif @@ -43,6 +44,8 @@ static STREAM *oldstream = 0; static UBYTE underscore[2] = {'_',0}; static PREVAR *ThePreVar = 0; +static int ExitDoLoops(int, const char *); + static KEYWORD precommands[] = { {"add" , DoPreAdd , 0, 0} ,{"addseparator" , DoPreAddSeparator,0,0} @@ -2783,9 +2786,22 @@ int DoTerminate(UBYTE *s) #[ DoContinueDo : */ +/** + * Jumps forward to the corresponding `#enddo` of the specified number of outer `#do` loops. + * + * @par Syntax: + * @code + * #continuedo [=1] + * @endcode + * + * If `number` is omitted, it defaults to 1. + * If `number` is zero then the instruction has no effect. + */ int DoContinueDo(UBYTE *s) { DOLOOP *loop; + WORD levels; + int result; if ( AP.PreSwitchModes[AP.PreSwitchLevel] != EXECUTINGPRESWITCH ) return(0); if ( AP.PreIfStack[AP.PreIfLevel] != EXECUTINGIF ) return(0); @@ -2795,6 +2811,37 @@ int DoContinueDo(UBYTE *s) return(1); } + SkipSpaces(&s); + if ( *s == 0 ) { + levels = 1; + } + else if ( FG.cTable[*s] == 1 ) { + ParseNumber(levels,s); + SkipSpaces(&s); + if ( *s != 0 ) goto improper; + } + else { +improper: + MesPrint("@Improper syntax of %#continuedo instruction"); + return(1); + } + + if ( levels > NumDoLoops ) { + MesPrint("@Too many loop levels requested in %#continuedo instruction"); + return(1); + } + + result = ExitDoLoops(levels-1,"continuedo"); + if ( result != 0 ) return(result); + + if ( levels <= 0 ) return(0); + + if ( AC.CurrentStream->type == PREREADSTREAM3 + || AP.PreTypes[AP.NumPreTypes] == PRETYPEPROCEDURE ) { + MesPrint("@Trying to jump out of a procedure with a %#continuedo instruction"); + return(1); + } + loop = &(DoLoops[NumDoLoops-1]); AP.NumPreTypes = loop->NumPreTypes+1; AP.PreIfLevel = loop->PreIfLevel; @@ -3031,7 +3078,6 @@ illdo:; int DoBreakDo(UBYTE *s) { - DOLOOP *loop; WORD levels; if ( AP.PreSwitchModes[AP.PreSwitchLevel] != EXECUTINGPRESWITCH ) return(0); @@ -3062,6 +3108,19 @@ int DoBreakDo(UBYTE *s) MesPrint("@Too many loop levels requested in %#breakdo instruction"); Terminate(-1); } + return(ExitDoLoops(levels,"breakdo")); +} + +/** + * Exits the specified number of nested `#do` loops. + * + * @param levels The number of loops to exit. + * @param instruction The instruction name used for error reporting. + * @return 0 on success, nonzero on error. + */ +static int ExitDoLoops(int levels, const char *instruction) +{ + DOLOOP *loop; while ( levels > 0 ) { while ( AC.CurrentStream->type != PREREADSTREAM && AC.CurrentStream->type != PREREADSTREAM2 @@ -3072,16 +3131,13 @@ int DoBreakDo(UBYTE *s) && AP.PreTypes[AP.NumPreTypes] != PRETYPEPROCEDURE ) AP.NumPreTypes--; if ( AC.CurrentStream->type == PREREADSTREAM3 || AP.PreTypes[AP.NumPreTypes] == PRETYPEPROCEDURE ) { - MesPrint("@Trying to jump out of a procedure with a %#breakdo instruction"); - Terminate(-1); + MesPrint("@Trying to jump out of a procedure with a %#%s instruction",instruction); + return(1); } loop = &(DoLoops[NumDoLoops-1]); AP.NumPreTypes = loop->NumPreTypes; AP.PreIfLevel = loop->PreIfLevel; AP.PreSwitchLevel = loop->PreSwitchLevel; -/* - AP.NumPreTypes--; -*/ NumDoLoops--; DoUndefine(loop->name); M_free(loop->p.buffer,"loop->p.buffer");