0

Short Version

Given:

    const
       Whitespace = [$0009, $000A, $000C, $0020];
  • Works: if ch in Whitespace then...
  • Fails: case ch of Whitespace: ...

Long Version

Normally, if you were to have a case statement, you can include various values for each case, eg:

var
  ch: UCS4Char; // unsigned 32-bit

case ch of
48..57: {asciiDigit}; //i.e. '0'..'9'
65..70: {asciiUpperHexDigit}; //i.e. 'A'..'F'
97..102: {asciiLowerHexDigit}; //i.e. 'a'..'f'
end;

That works, but I would like these values to be constants:

const
  //https://infra.spec.whatwg.org/#code-points
  asciiDigit         = [Ord('0')..Ord('9')]; //https://infra.spec.whatwg.org/#ascii-digit
  asciiUpperHexDigit = [Ord('A')..Ord('F')]; //https://infra.spec.whatwg.org/#ascii-upper-hex-digit
  asciiLowerHexDigit = [Ord('a')..Ord('f')]; //https://infra.spec.whatwg.org/#ascii-lower-hex-digit
  asciiHexDigit      = asciiUpperHexDigit + asciiLowerHexDigit; //https://infra.spec.whatwg.org/#ascii-hex-digit
  asciiUpperAlpha    = [Ord('A')..Ord('Z')]; //https://infra.spec.whatwg.org/#ascii-upper-alpha
  asciiLowerAlpha    = [Ord('a')..Ord('z')]; //https://infra.spec.whatwg.org/#ascii-lower-alpha
  asciiAlpha         = asciiUpperAlpha + asciiLowerAlpha; //https://infra.spec.whatwg.org/#ascii-alpha
  asciiAlphaNumeric  = asciiDigit + asciiAlpha; //https://infra.spec.whatwg.org/#ascii-alphanumeric

Is there any arrangement of any syntax that will allow:

  • caseing a Cardinal
  • against a "set of Cardinals"?

Or am I permanently stuck with the following?

var
  ch: UCS4Char; //unsigned 32-bit

case ch of
Ord('!'): FState := MarkupDeclarationOpenState;
Ord('/'): FState := EndTagOpenState;
Ord('?'): AddParseError('unexpected-question-mark-instead-of-tag-name');
UEOF: AddParseError('eof-before-tag-name parse error');
Whitespace: FState := SharkTankContosoGrobber;
else
  if ch in asciiDigit then
  begin
    FReconsume := True;
    FState := tsTagNameState;
  end
  else
    AddParseError('invalid-first-character-of-tag-name parse error');
end;

Obviously, using the conceptual case matches the logic being performed; having to do if-elseif is...lesser.

Note: I don't need it to actually be a Delphi "set", that is a specific term with a specific meaning. I just want:

case ch of Whitespace: ...

to work the same way:

if ch in Whitespace then...

already does work.

And we know the compiler already is OK with comparing a Cardinal to a "set", because the following already works:

case ch of
$000A, $000D, $0009, $0032: ...
end;

It's comparing a Cardinal to a "set of numbers".

Bonus Reading

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219

1 Answers1

2

No, this is not supported.

According to the official documentation:

A case statement has the form:

case selectorExpression of
  caseList1: statement1;
   ...
  caseListn: statementn;
end

where [...] each caseList is one of the following:

  • A numeral, declared constant, or other expression that the compiler can evaluate without executing your program. It must be of an ordinal type compatible with selectorExpression. [...]
  • A subrange having the form First..Last, where First and Last both satisfy the criterion above and First is less than or equal to Last.
  • A list having the form item1, ..., itemn, where each item satisfies one of the criteria above.

Hence, this only allows single values, explicit ranges, and lists of such values, as part of the case syntax.

Although the Delphi documentation is good, it isn't perfect and you cannot rely on it to 100%. However, I'm sure all experienced Delphi developers will agree that a caseList cannot be a predeclared single-identifier "collection" of ordinal values compatible with selectorExpression.

You may file a feature request at Embarcadero's Jira.


But you can use the range syntax and a previously declared subrange type (not set constant) to achieve something partly similar:

type
  TAsciiDigit = '0'..'9';
  TAsciiLatinCapitalLetter = 'A'..'Z';
  TAsciiLatinSmallLetter = 'a'..'z';

procedure TForm1.FormCreate(Sender: TObject);
begin

  var c := 'R';

  case c of
    Low(TAsciiDigit) .. High(TAsciiDigit):
      ShowMessage('Digit!');
    Low(TAsciiLatinCapitalLetter) .. High(TAsciiLatinCapitalLetter):
      ShowMessage('Capital!');
    Low(TAsciiLatinSmallLetter) .. High(TAsciiLatinSmallLetter):
      ShowMessage('Small letter!');
  else
    ShowMessage('Something else.');
  end;

end;

Bonus remark: In fact, the non-100% accuracy of the documentation can be seen in the section quoted above:

selectorExpression is any expression of an ordinal type smaller than 32 bits

That's nonsense. selectorExpression certainly can be a 32-bit integer.

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384