Menu

#2657 A multi-line clause (aka compound statement) associated with an IF/ELSE command is interpreted as one line of commands - this affects string dereferencing using the @ token

None
closed-not-a-bug
nobody
None
2023-09-30
2023-09-26
najevi
No

The current behaviour may very well be by design however, the current behaviour seems counter-intuitive to me. From the IF command description in the manual:

This version of gnuplot supports block-structured if/else statements.
Now a multi-line clause may be enclosed in curly brackets.

The same behaviour is observed within a main script block and also within a function block so it appears to be a feature of any { .. } delimited, multiple line compound statement (a.k.a. multi-line clause) associated with an IF/ELSE statement.

It seems counter-intuitive that a string that is (apparently) defined on some "line" before it is used alongside the de-reference token, "@", should generate a warning: "<stringvar> is not a string variable" </stringvar>

To be fair, the man page regarding "Substitution of string variables as macros" does allude to this via two sentences:

Macro expansion is handled as the very first thing the interpreter does when looking at a new line of commands and is only done once.
Macro expansion inside a bracketed iteration occurs before the loop is executed;

Either of these might be interpreted as also applying to a multi-line clause (compound statement) associated with an IF/ELSE command. It seems to me that if a block-structured IF/ELSE statements are to be supported then commands on separate lines of a multi-line clause ought to be interpreted separately and therefore benefit from substitution of a previously defined string variable as a macro.

## IF/ELSE compound statement treated as one line - affects string expansion as macro using @ token
##  (gnuplot v6 rc1  on Windows 11)
##
## To experience each bug uncomment that/those script line(s) that has/have just a single hash '#'.
##
##(5) a compound statement associated with IF/ELSE is treated as one line
##(5a) this causes a string defined apparently before a plot command to fail to dereference when using "@string" for macro expansion
##(5c) error messages also refere to the line number of the IF/ELSE statement rather than the line number within the compound {..} statement 
##(5d) the behaviour is the same within the main script block or within a function block

function $yourPlot(strSource,sizeFont) << _EOFD
    local ARG0 = "$yourPlot"    # hack
    set title strSource." - variable color and orientation in plotstyle 'with labels'" offset 0,-2

    ## a compound statement associated with IF/ELSE is treated as one line so string dereferencing 
    ##  (i.e. string expansion as a macro) fails unless string is defined before the IF/ELSE
    str_earlyFont = sprintf("\", %d\"", sizeFont)
    #str_lateFont = sprintf("\"Times,%d\"", sizeFont)
    if (sizeFont) {
        str_lateFont = sprintf("\"Times,%d\"", sizeFont)
        plot @strSource using 1:1:2:3:0 with labels rotate variable tc variable font @str_lateFont
        }
    else {
        plot @strSource using 1:1:2:3:0 with labels rotate variable tc variable font @str_earlyFont
        }

    return sprintf("function, %s, completed; with %d arguments", ARG0, ARGC)
_EOFD

##
## Additional data columns can be used to hold text rotation or color
## for plot style "with labels"
##

demo = 1

$DataEarly <<EOD
1 one -30
2 two -60
3 three -90
4 four -120
5 five -150
6 six -180
7 seven -210
8 eight -240
9 nine -270
10 ten -300
11 eleven -330
12 twelve -360
EOD

$DataLate <<EOD
1 one -30
2 two -60
3 three -90
4 four -120
5 five -150
6 six -180
7 seven -210
8 eight -240
9 nine -270
10 ten -300
11 eleven -330
12 twelve -360
EOD

set angle degrees
unset key
set title "variable color and orientation in plotstyle 'with labels'" offset 0,-2

set xrange [0:13]
set yrange [0:13]
set xtics 1,1,12 nomirror
set ytics 1,1,12 nomirror
set border 3

printerr $yourPlot("$DataEarly",0)
pause mouse close

## a compound statement associated with IF/ELSE is treated as one line so string dereferencing 
##  (i.e. string expansion as a macro) fails unless string is defined before the IF/ELSE
string_earlyFont = "\"Times,18\""
#string_lateFont = "\"Times,22\""

if (demo) {
    set title "EARLY - variable color and orientation in plotstyle 'with labels'" offset 0,-2
    plot $DataEarly using 1:1:2:3:0 with labels rotate variable tc variable font @string_earlyFont
    pause mouse close

    set title "LATE - variable color and orientation in plotstyle 'with labels'" offset 0,-2
    ## a string defined within an IF/ELSE compound statement cannot be dereferenced within the same compound statement
    string_lateFont = "\"Times,10\""    
    plot $DataLate using 1:1:2:3:0 with labels rotate variable tc variable font @string_lateFont
    pause mouse close
    }

printerr $yourPlot("$DataLate",16)
pause mouse close

Discussion

  • Ethan Merritt

    Ethan Merritt - 2023-09-28

    The implementation of string macros predates the introduction of bracketed clauses by at least a decade. It dates back to gnuplot version 4.2 (2007). In retrospect I don't think introduction of macros was a great idea, but that's water under the bridge. So much has been added to the program since then to support the use of string variables in expressions, function parameters, etc, that very few cases remain where the use of a string macro is necessary or preferred.

    What exactly are you trying to do? I don't see anything in your script that explains why you are using the @ operator at all. For example where you have

    string_lateFont = "\"Times,10\""    
    plot $DataLate using 1:1:2:3:0 with labels ... font @string_lateFont
    

    the macro expansion is pointless. Is there a reason not to use the string directly?

    lateFont = "Times,10"    
    plot $DataLate using 1:1:2:3:0 with labels ... font lateFont
    

    That works in a bracketed clause with no problem.

     
    • najevi

      najevi - 2023-09-29

      I am new to gnuplot so I don't have the benefit of historical hindsight.

      Yes, that script was necessarily dumbed down to only demonstrate a handful of error types that I wanted to report.
      Where you read if (demo) { I was originally testing an integer variable (not even a string!) for a non-zero value; and then, inside the if { }-clause, was using that non-zero value as a font-size to construct a "font, size" string to use after the font keyword.
      If the (integer) value was 0 then I would call a version of plot that does not have the with labels style.
      I don't explain my proclivity for the @ operator (string macro). My reply below explains how that preference came about.

      In hindsight, the string macro example (using font "<name>{,<size>}") that I ran with to illustrate the IF-related bug was not a good one because it turns out that string expansion into a command line macro can be easily avoided .. and replaced by a plain old string variable.
      The same cannot be said for the many "with <style>" color options that make reference to any one of "colorname" or <colorspec> or <n>.

      Color confusion is all that I have come across so far.
      In that confusion, I wrongly tarred font "<name>{,<size>}" with the same brush. My bad!

      What exactly are you trying to do? I don't see anything in your script that explains why you are using the @ operator at all.

      Prompted by this remark I endeavoured to eliminate from recent scripts as many instances of @strVar as I could;
      replacing them by just a plain old strVar reference. In most cases this was easy enough to do.
      So I really do take your point to heart.

      The remaining problematic cases involved the interpreter throwing the error: "colorspec option not recognized"
      This comes about when the string value is any of those described in numbered items 2 thru 6 below.

      I think that the versatility of using a string variable as a command line macro (string macro) lies in this very powerful sentence, highlighted in bold below.

      Substitution of string variables as macros
      The character @ is used to trigger substitution of the current value of a string variable into the command
      line. The text in the string variable may contain any number of lexical elements. This allows string variables
      to be used as command line macros. Only string constants may be expanded using this mechanism, not
      string-valued expressions.


      Take any of the bold cases alluded to earlier:
      ... {linecolor | lc} {"colorname" | <colorspec> | <n>}
      ... {textcolor | tc} {<colorspec> | {linetype | lt} <n>}
      ... {fillcolor | fc} {<colorspec> | linetype <n> | linestyle <n>}
      within a plot-command that specifies a with-style that allows for one or more of these color options.

      1. When the string strVar = "7" (or the integer intVar = 7) is used in place of <n> the color corresponding to linetype 7 in the test term output is applied to either line or text or fill. This is good.
      2. When the string strVar = "blue" is used the expected color is not applied .. even when the strVar is expanded using a string macro (i.e. @strVar).
      3. The remedy in this case is strVar = "\"blue\"" but once again this does not behave for a plain old string .. unless the strVar is expanded using a string macro.
      4. When the string strVar = "palette" (or strVar = "palette z") is used the variable color based on the last column of data is not applied .. unless the strVar is expanded using a string macro.
      5. When the string strVar = "variable" is used the variable color based on the last column of data is not applied .. unless the strVar is expanded using a string macro.
      6. When the string strVar = "rgbcolor variable" is used the variable color based on the last column of data is not applied .. unless the strVar is expanded using a string macro.

      I have several 2 or 3 line scripts to demonstrate each of the above anomalies but am inclined to hold off from sharing all of that here since it really has nothing to do with the IF statement issue being addressed.
      Instead I propose to do so as a separate bug report that addresses just that topic of apparent inconsistency of handling colorspec strings.

      In cases 2 thru 6 it is almost as though palette, variable, "rgbcolor variable" and color-name are handled as a lexical keyword.
      The interpreter appears to prefer to see that particular string value expanded as a string macro,
      i.e. expanded before before parsing for lexical elements.

      ... for a newcomer to the tool this all appears quite confusing.
      Discovering the "method in the madness" comes with experience, I suppose.
      In the interim human nature is to utilize the first working syntax that gets done whatever is the immediate task at hand.

      • In my limited experience to date, I have the impression that the interpreter appears to prefer/tolerate a string macro in more situations than it will tolerate a plain old string.

      With more experience using gnuplot I suspect I will learn of alternative ways (using string variables in expressions, function parameters, etc.) of achieving the equivalent capability of a string macro.
      I suspect that eval string will emerge as a useful tool in that endeavor.
      For now, I tend to utilize the string macro out of expedience ... or perhaps impatience!

      The @string expansion (string macro) method has high utility because it is a more elemental/atomic version of the eval longer_string feature -
      which I understand expects to be first evaluated and then parsed/handled as one complete command rather than as just one small portion of a command.

       
      • Ethan Merritt

        Ethan Merritt - 2023-09-29

        In cases 2 thru 6 it is almost as though palette, variable, "rgbcolor variable" and color-name are handled as a lexical keyword.
        The interpreter appears to prefer to see that particular string value expanded as a string macro, i.e. expanded before before parsing for lexical elements.

        That is exactly correct. These are lexical elements rather than string variables. And that was indeed the original reason for the introduction of macro expansion, because back in the day there were many fewer places where a string variable was accepted by the parser. There may well be exceptions (corrections welcome!) but in theory any place in the documentation that defines the syntax of a command will show lexical elements as regular text and command tokens that may be provided as variables or expressions in angle brackets or quotes. For example, from "help colorspec"

        Syntax:
        
               ... {linecolor | lc} {"colorname" | <colorspec> | <n>}
               ... {textcolor | tc} {<colorspec> | {linetype | lt} <n>}
               ... {fillcolor | fc} {<colorspec> | linetype <n> | linestyle <n>}
        
         where <colorspec> has one of the following forms:
        
               rgbcolor "colorname"    # e.g. "blue"
               rgbcolor "0xRRGGBB"     # string containing hexadecimal constant
               rgbcolor "0xAARRGGBB"   # string containing hexadecimal constant
               rgbcolor "#RRGGBB"      # string containing hexadecimal in x11 format
               rgbcolor "#AARRGGBB"    # string containing hexadecimal in x11 format
               rgbcolor <integer val>  # integer value representing AARRGGBB
               rgbcolor variable       # integer value is read from input file
               palette frac <val>      # <val> runs from 0 to 1
               palette cb <value>      # <val> lies within cbrange
               palette z
               palette <colormap>      # use named colormap rather than current palette
               variable                # color index is read from input file
               bgnd                    # background color
               black
        
         
      • Ethan Merritt

        Ethan Merritt - 2023-09-29

        [continued]
        Specifically with regard to the use of macros to expand color names. Yes - this was a point that has caused confusion before. The color names were implemented as lexical elements rather strings. To be fair, they were introduced in version 4.0 before string variables even existed as an alternative.

        To mitigate this limitation, gnuplot version 6 introduces a function rgbcolor() that accepts the color name as a string and returns the hexadecimal representation of that color as an integer. So the following is now possible:

        array Rainbow[7] = [ "red", "orange", "yellow", "green", "blue", "dark-blue", "violet"]
        plot for [i=1:7] sin(x + i) with lines lc rgbcolor( Rainbow[i] )
        

        We'll see whether this reduces confusion or increases it :-).

         
  • Ethan Merritt

    Ethan Merritt - 2023-09-30
    • status: open --> closed-not-a-bug
    • Group: -->
    • Priority: -->
     

Log in to post a comment.