Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2007-02

"case" with range stupidities

8 messages · 3 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Vinzent Hoefler

Hi all,

consider the following program:

-- 8< --

program
   Case_Test;
type
   My_Range = 2000 .. 3000;
var
   X : Integer;
begin
   X := 2500;
   case X of
      Low (My_Range) .. High (My_Range) : WriteLn ('In range. (1)');
      My_Range                          : WriteLn ('In range. (2)');
   else
      WriteLn ('Out of range.');
   end {case};
end {Case_Test}.

-- 8< --

Is there any reasoning why the first switch in the case statement ("Low (My_Range) ...") is allowed, whilst the second one (just "My_Range") is not (says: "constant expression expected")?

Another minor issue is that a type declaration like:

   type
      My_Range = (2500 - 500) .. (2500 + 500);

is not accepted (message similar to ";" expected, ".." found) and (basically the same declaration)

   type
      My_Range = 2500 - 500 .. 2500 + 500;

is. Sometimes I'd really like to add those parantheses just to make the intention more clear.

Regards,

Vinzent.

Reply by email

Daniël Mantione
to Vinzent Hoefler

Op Thu, 15 Feb 2007, schreef Vinzent Hoefler:

Show quoted text 25 lines
program Case_Test; type My_Range = 2000 .. 3000; var X : Integer; begin X := 2500; case X of Low (My_Range) .. High (My_Range) : WriteLn ('In range. (1)'); My_Range : WriteLn ('In range. (2)'); else WriteLn ('Out of range.'); end {case}; end {Case_Test}. -- 8< -- Is there any reasoning why the first switch in the case statement ("Low (My_Range) ...") is allowed, whilst the second one (just "My_Range") is not (says: "constant expression expected")?

Well, a case label is basically a set of values. A type definition is something different, i.e. you cannot use "byte" or "longint" as case labels either. Afaik set names aren't allowed either but allowing these would seem more logical to me than allowing type names (seeing byte and longint as case labels would seem weird to me.

Show quoted text 13 lines
Another minor issue is that a type declaration like: type My_Range = (2500 - 500) .. (2500 + 500); is not accepted (message similar to ";" expected, ".." found) and (basically the same declaration) type My_Range = 2500 - 500 .. 2500 + 500; is. Sometimes I'd really like to add those parantheses just to make the intention more clear.

I think this is a parser limitation. I don't think it is a problem to allow it.

Daniël

Reply by email

Vinzent Hoefler
to Daniël Mantione
Daniël Mantione wrote 30 lines
Op Thu, 15 Feb 2007, schreef Vinzent Hoefler: > program > Case_Test; > > type > My_Range = 2000 .. 3000; > > var > X : Integer; > > begin > X := 2500; > > case X of > Low (My_Range) .. High (My_Range) : WriteLn ('In range. > (1)'); My_Range : WriteLn ('In range. > (2)'); else > WriteLn ('Out of range.'); > end {case}; > end {Case_Test}. > > -- 8< -- > > Is there any reasoning why the first switch in the case statement > ("Low (My_Range) ...") is allowed, whilst the second one (just > "My_Range") is not (says: "constant expression expected")? Well, a case label is basically a set of values. A type definition is something different, i.e. you cannot use "byte" or "longint" as case labels either.

Well, fair enough, but why is that I can declare a

   type
      Foo = array[byte] of Something;

where the type name "byte" also indicates the range? Considering that

   type
      Foo = array[Low (byte) .. High (byte) of Something;

is totally equivalent, the allowed usage of range types seems a little bit inconsistent...

Vinzent.

Reply by email

Daniël Mantione
to Vinzent Hoefler

Op Thu, 15 Feb 2007, schreef Vinzent Hoefler:

Show quoted text 12 lines
Well, fair enough, but why is that I can declare a type Foo = array[byte] of Something; where the type name "byte" also indicates the range? Considering that type Foo = array[Low (byte) .. High (byte) of Something; is totally equivalent, the allowed usage of range types seems a little bit inconsistent...

Yes, but as I said, case labels are a set, not just a range:

const x=[0,1,5];

type foo=array[x] of something

...would again be rather weird.

Daniël

Reply by email

Vinzent Hoefler
to Daniël Mantione
Daniël Mantione wrote 22 lines
Op Thu, 15 Feb 2007, schreef Vinzent Hoefler: > Well, fair enough, but why is that I can declare a > > type > Foo = array[byte] of Something; > > where the type name "byte" also indicates the range? Considering > that > > type > Foo = array[Low (byte) .. High (byte) of Something; > > is totally equivalent, the allowed usage of range types seems a > little bit inconsistent... Yes, but as I said, case labels are a set, not just a range: const x=[0,1,5]; type foo=array[x] of something ...would again be rather weird.

It would be, yes. It even seems constants can't be used in an array type declaration at all (well, not in a way different from defining the lower and upper bound, I mean) and

   const
      X = 0 .. 5;

does not work either while "type X = (Zero, One, Five)" would do both cases. ;)

So in short: it seems a range type like "byte" can be used where a "pure" range is expected, but it cannot be used where a set is expected (like for a case label, even though there a set constant would not be accepted either):

-- 8< --
type
   X = (Zero, One, Five);
var
   Bla : X;
const
   Foobar = [Zero, One];
begin
   Bla := Five;
   case Bla of
      Low (Foobar) .. High (Foobar) : WriteLn ('Less than Five.');
      // The following two lines don't work, of course.
      //Foobar                        : WriteLn ('NO.');
      //[Foobar]                      : WriteLn ('NO.');
   else
      WriteLn ('More than One, I guess.');
   end {case};
   // A real set operation.
   if Bla in Foobar then
      WriteLn ('Really less than Five.')
   else
      WriteLn ('Wrong. It is NOT less than five!');
end.
-- 8< --

So if the case label is considered a set why can't I use a set constant (see the commented lines above) and if I do the range thing, which compiles, it does not consider the value of the constant but rather the range of its /type/...?

Seems to me that case labels are very special things, at least.

Another oddity would be a for loop:

   for Bla := One to Five do ...

would be a range only, yes? (Apart from the fact that - due to the "to" keyword - really isn't).

So wouldn't it be easier to write

|  for Bla := X do ...

here, when X is some range type, I'd like to iterate over? Again I have to Write "Low (X) to High (X)" quite similar to the "case" case.

I suppose changing that behaviour regarding range types is not an option at all, so I'd ask for some other improvement:

*deep breath*

What about a compiler built-in like Low() and High() called Range() where its return type indicates something that would be compatible to a case and for label and just denote the range of the whole type?

Basically a short cut to the "Low (Type) ..|to High (Type)" expression, but without the possible pitfall of writing

Low (Type) .. High (A_Similar_And_Unfortunately_Compatible_Type)

and thus being less error-prone...?

I mean, doing:

|for Bla := Range (X) do

or

|case Bla in
|Range (X): ...

doesn't actually look unreadable to me...

Regards,

Vinzent.

Reply by email

Florian Klaempfl
to Daniël Mantione: Op Thu, 15 Feb 2007, schreef Vinzent Hoefler: Well, a case label is basically a set of…
Show quoted text 16 lines
> Another minor issue is that a type declaration like: > > type > My_Range = (2500 - 500) .. (2500 + 500); > > is not accepted (message similar to ";" expected, ".." found) and > (basically the same declaration) > > type > My_Range = 2500 - 500 .. 2500 + 500; > > is. Sometimes I'd really like to add those parantheses just to make the > intention more clear. I think this is a parser limitation. I don't think it is a problem to allow it.

It is a problem. It's simply an inconsitency of pascal. When the parser encounters
type
My_Range = (

it expects an enum declaration. Pascal is parsed without look ahead so this can't be solved cleanly, just consider

in one unit
const
   a = 1;
   b = 2;

in another unit

type
   atype = (a) .. (b);

Only at the .. the compiler could detect that this is an ord type declaration.

Reply by email

Daniël Mantione
to Vinzent Hoefler: It would be, yes. It even seems constants can't be used in an array type declaration at…

Op Thu, 15 Feb 2007, schreef Vinzent Hoefler:

So if the case label is considered a set why can't I use a set constant (see the commented lines above)

Because a normal set is limited by brackets:

const a = [1,2,5];
b = [a,7,8,9]; {This is not allowed.}
b = [a]+[7,8,9]; {But this is.}

Because, in a case statement you are already "inside" the brackets:

case x of
  1,2,5: {No brackets.}

... you have a syntax problem for allowing set constants (what does "[1,2,4],9" mean?). If you would design a syntax for it, you could allow set constants.

and if I do the range thing, which compiles, it does not consider the value of the constant but rather the range of its /type/...?

No, low(x) is a constant.

Show quoted text 8 lines
Seems to me that case labels are very special things, at least. Another oddity would be a for loop: for Bla := One to Five do ... would be a range only, yes? (Apart from the fact that - due to the "to" keyword - really isn't).

It is a range, but one also needs to specify the direction (to/downto), so it is logical that for-loops don't use .. syntax.

Show quoted text 6 lines
So wouldn't it be easier to write | for Bla := X do ... here, when X is some range type, I'd like to iterate over? Again I have to Write "Low (X) to High (X)" quite similar to the "case" case.

It would be easier but you would miss the direction information.

Show quoted text 15 lines
I suppose changing that behaviour regarding range types is not an option at all, so I'd ask for some other improvement: *deep breath* What about a compiler built-in like Low() and High() called Range() where its return type indicates something that would be compatible to a case and for label and just denote the range of the whole type? Basically a short cut to the "Low (Type) ..|to High (Type)" expression, but without the possible pitfall of writing Low (Type) .. High (A_Similar_And_Unfortunately_Compatible_Type) and thus being less error-prone...?

It looks okay, but does this pitfall actually exist? low(colour)..high(wind_direction) would be a type violation.

I mean, doing: |for Bla := Range (X) do

See direction problem.

Show quoted text 6 lines
or |case Bla in |Range (X): ... doesn't actually look unreadable to me...

Indeed, but do you solve a problem? I.e. I can imagine some kind of syntax to allow set constants would help, but this seems mostly a typing shortcut.

Daniël

Reply by email

Vinzent Hoefler
to Daniël Mantione
Daniël Mantione wrote
Op Thu, 15 Feb 2007, schreef Vinzent Hoefler: Because, in a case statement you are already "inside" the brackets:

Yeah, I sort of figured that. ;)

Show quoted text 9 lines
> Another oddity would be a for loop: > > for Bla := One to Five do ... > > would be a range only, yes? (Apart from the fact that - due to the > "to" keyword - really isn't). It is a range, but one also needs to specify the direction (to/downto), so it is logical that for-loops don't use .. syntax.

Oh, right, I forgot that. Damn.

That's why in Ada you can write

for Bla in X'Range loop

vs.

for Bla in reverse X'Range loop

for the intended direction of the given range.

Show quoted text 13 lines
> What about a compiler built-in like Low() and High() called Range() > where its return type indicates something that would be compatible > to a case and for label and just denote the range of the whole > type? > > Basically a short cut to the "Low (Type) ..|to High (Type)" > expression, but without the possible pitfall of writing > > Low (Type) .. High (A_Similar_And_Unfortunately_Compatible_Type) > > and thus being less error-prone...? It looks okay, but does this pitfall actually exist?

Yes, it does. Especially when copying and pasting. ;)

The reason why I stumbled over that was that I changed a case statement from a perfect fit:

case Frequency of
1600: Idx = Hz_1600;
...

where the returned Idx is an index to an array of hardware parameters to generate that frequency. For some unpublishable reason I changed that from a perfect match (here: 1600) to a range (say: 1500 .. 1700) and I started declaring range types for that because putting in the range into the case label seemed too bad for later changes.

So then I ended up writing:

case Frequency of
Low (Range_1_6_K) .. High (Range_1_6_K) : Idx := ...
Low (Range_1_8_K) .. High (Range_1_8_K) : Idx := ...
  ...

for quite some values. Of course you start copy- and pasting things then and I had to triple check if I really matched the Low(...) with the according High(...) parts and the correct value for Idx.

low(colour)..high(wind_direction) would be a type violation.

Yes. But integer ranges are compatible in any case and that's what I am bound to here (in fact, the code even "kind of" changes those integers to a type-safe enumeration type for further uses).

Show quoted text 10 lines
> or > > |case Bla in > |Range (X): ... > > doesn't actually look unreadable to me... Indeed, but do you solve a problem? I.e. I can imagine some kind of syntax to allow set constants would help, but this seems mostly a typing shortcut.

Well, it is.

The above mentioned lines of Ada code can still be expressed as

for Bla in [reverse] X'Low .. X'High loop

and would still mean the same. And due to the fact that in Pascal such a Range() built-in would still not be useable in a for loop, it seems, its use would be too limited to be considered an improvement. Unless I'd get Reverse_Range(), too. ;)

Regards,

Vinzent.

Reply by email