Use of semicolons with if/else and try/finally blocks - delphi

I have found that this code compiles both with and without a semicolon at the places pointed out below.
What is the correct way to use semicolons here?
try
try
if MyBoolean = True then
begin
DoSomething;
end
else
begin
DoSomethingElse;
end <<<--- Semi colon here?
except
;
end <<<--- Semi colon here?
finally
;
end;

This is documented behavior:
Declarations and Statements (Delphi): Compound Statements
A compound statement is a sequence of other (simple or structured) statements to be executed in the order in which they are written. The compound statement is bracketed by the reserved words begin and end, and its constituent statements are separated by semicolons. For example:
begin
Z := X;
X := Y;
X := Y;
end;
The last semicolon before end is optional. So this could have been written as:
begin
Z := X;
X := Y;
Y := Z
end;
And also:
Delphi's Object Pascal Style Guide: Statements
Statements are one or more lines of code followed by a semicolon. Simple statements have one semicolon, while compound statements have more than one semicolon and therefore consist of multiple simple statements.
Here is a simple statement:
A := B;
If you need to wrap the simple statement, indent the second line two spaces in from the previous line. Here is a compound, or structured, statement:
begin
B := C;
A := B;
end;
Compound Statements always end with a semicolon, unless they immediately precede an end keyword, in which case the semicolon is optional but recommended by this style guide.
An "end keyword" in your example would include except and finally.

Related

Case statement against constant set of integers?

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
Delphi case statement for integer ranges
Efficiently compare an integer against a static list of integers in Delphi?
any way to compare an integer variable to a list of integers in if statement
What Delphi type for 'set of integer'?
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.

Boolean expression order of evaluation in Delphi?

if exp1 and exp2 and exp3 then
//something
In Delphi is the order of evaluation exp1, exp2 and exp3 (of course they are all Boolean) is defined or random?
The order of evaluation is only defined when Boolean Short-Circuit Evaluation is enabled. Then, as the documentation in Complete Versus Short-Circuit Boolean Evaluation explains, evaluation is left to right.
If Boolean Short-Circuit Evaluation is not enabled, the order is undefined. The following code demonstrates this:
{$APPTYPE CONSOLE}
function A: boolean;
begin
Result := True;
Write('A ');
end;
function B: string;
begin
Result := '';
Write('B ');
end;
function C: Integer;
begin
Result := 0;
Write('C ');
end;
begin
{$O+}
Writeln('short circuit on');
{$B-}
if A and (B = '') and (C = 0) then Writeln;
Writeln('short circuit off');
{$B+}
if A and (B = '') and (C = 0) then Writeln;
end.
For the first one, you get printed A B C, and for the second one you get B A C.
And that was compiled for Win32 - to make this spicy, and bring home the point of this being Undefined, lets run it on Win64 where we get A B C in both cases.
You might say: "Ok, but maybe just B was called first but the evaluation of the boolean expression B = '' is evaluated in the correct order." Let's take a look at the assembler code that gets executed:
if A and (B = '') and (C = 0) then Writeln;
0040B17C 8D45E8 lea eax,[ebp-$18]
0040B17F E864EAFFFF call B
0040B184 837DE800 cmp dword ptr [ebp-$18],$00
0040B188 0F94C3 setz bl
0040B18B E81CEAFFFF call A
0040B190 22D8 and bl,al
0040B192 E891EAFFFF call C
0040B197 85C0 test eax,eax
0040B199 0F94C0 setz al
0040B19C 22D8 and bl,al
Nope:
call B, compare to ''
call A, and it with the result of the string comparison
call C, compare with 0, and it with the result of the previous and
Which translates to (written in left to right order):
((B = '') and A) and (C = 0)
Addendum: Because I saw this mentioned in another answer and discussed in the comments. Parentheses are no solution to force order of evaluation of operands. You can only group operations but the compiler might still decide to evaluate the right operand first.
In the code above there is no way to put any parentheses to get A B C simply because the compiler flipped the first two operands. It then however executed the operators from left to right and that is something to easily confuse - the question was not whether the first or the second and was executed first but the order of the boolean expressions - the operands.
The documentation will guide you of course.
I have found that in more complex if statements the compiler does not do what even experienced programmers expected when they wrote the code, often because they are including logical nots and mixing boolean ands and ors. Remembering the precedence and association rules is not always easy.
My tip is to be explicit about what you want and put brackets around the terms. The compiler won't mind and it makes it clear what you intended when you or someone else reviews the code.
With compound if statements laying out the logical parts together can aso help humans to understand what was meant.
For example:
if( ((pObjectRef<>nil) And (pObjecRef.Property=SomeValue)) Or
((pAlternateObject<>nil) And
((pAlternatObject.Property=AnotherValue) Or
(pAlternatObject.Property=YetAnotherValue))) ) then
begin
...
end

Missing operator or semicolon [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 7 years ago.
Improve this question
procedure TMainWin.AgeEBKeyPress(Sender: TObject; var Key: Char);
procedure ProcessInput(Sender:Tobject);
var age : byte;
begin
Age := StrToInt(AgeEB.Text);
Age := Age+2;
AnsLbl.visible := True //it's here, where there is a mistake
AnsLbl.Caption := 'Bad luck, I"m' + IntTostr(Age) + '-Years old'
if not (Key in ['0'..'9' , chr(vk_return), chr(vk_back)]) then begin
key := #0;
exit
end
This is the code, there is some thing wrong with it, but
Statements need to be separated using the separator symbol, ;.
In the following code, you fail to do this:
AnsLbl.visible := True
AnsLbl.Caption := 'Bad luck, I"m' + IntTostr(Age) + '-Years old'
if ...
You must place separators between these three statements. Typically they are placed at the end of each line like so:
AnsLbl.visible := True;
AnsLbl.Caption := 'Bad luck, I"m' + IntTostr(Age) + '-Years old';
if ...
But they need not be. You could equally write this as:
AnsLbl.visible := True
; AnsLbl.Caption := 'Bad luck, I"m' + IntTostr(Age) + '-Years old'
; if ...
That would be very weird and I am not advocating that you do so. The documentation says this:
Tokens
On the simplest level, a program is a sequence of tokens delimited by
separators. A token is the smallest meaningful unit of text in a
program. A separator is either a blank or a comment. Strictly
speaking, it is not always necessary to place a separator between two
tokens; for example, the code fragment:
Size:=20;Price:=10;
is perfectly legal. Convention and readability, however, dictate that
we write this in two lines, as:
Size := 20;
Price := 10;
Tokens are categorized as special symbols, identifiers, reserved
words, directives, numerals, labels, and character strings. A
separator can be part of a token only if the token is a character
string. Adjacent identifiers, reserved words, numerals, and labels
must have one or more separators between them.
The documentation also has this to say about compound statements:
Compound Statements
A compound statement is a sequence of other (simple or structured)
statements to be executed in the order in which they are written. The
compound statement is bracketed by the reserved words begin and end,
and its constituent statements are separated by semicolons. For
example:
begin
Z := X;
X := Y;
X := Y;
end;
The last semicolon before end is optional. So this could have been
written as:
begin
Z := X;
X := Y;
Y := Z
end;
Compound statements are essential in contexts where Delphi syntax
requires a single statement. In addition to program, function, and
procedure blocks, they occur within other structured statements, such
as conditionals or loops. For example:
begin
I := SomeConstant;
while I > 0 do
begin
...
I := I - 1;
end;
end;
You can write a compound statement that contains only a single
constituent statement; like parentheses in a complex term, begin and
end sometimes serve to disambiguate and to improve readability. You
can also use an empty compound statement to create a block that does
nothing:
begin
end;
It has been a while since i did any Delphi coding but I think you need ; semicolon at the end of each line.
Like for instance this line:
AnsLbl.visible := True
should be:
AnsLbl.visible := True;

If statement for checking strings

So I'm trying to determine if two different strings are the same with
if DerobModel.ConstructionCount > 22 then
begin
for i := 22 to DerobModel.ConstructionCount-1 do
begin
ConstructionName[i] := DerobModel.Constructions[i].Name;
ShowMessage(ConstructionName[i]);
ShowMessage(DerobModel.HouseProperties.StringValue['NWall']);
if ConstructionName[i]=DerobModel.HouseProperties.StringValue['NWall'] then
begin
ShowMessage('Hej');
igSurf[0]:=idWallCon[i];
end;
LayerCount[i] := DerobModel.Constructions[i].LayerCount;
idWallCon[i] := i+1;
end;
end;
The ShowMessage for both of the strings returns the same string but somehow it won't go in the if statement. Any ideas?
The = operator for strings is known to work. When strings s1 and s2 are equal, s1 = s2 evaluates true. Otherwise it evaluates false. The = operator has been known to work correctly in all versions of Delphi.
The conclusion to draw is that if the body of your if does not execute, then the two strings are not equal. Now that you know that the two strings are not equal, you can debug the program to work out why two things that you believed to be equal are in fact not equal.
Note that equality testing with = is exact. Letter case is significant. Whitespace is significant. And so on.
Your strings are different, simple as that.
If you want to figure out what exactly is different, you could write an else block portion to compare the strings in detail and show you exactly what is different.
if ConstructionName[i]=DerobModel.HouseProperties.StringValue['NWall'] then
begin
ShowMessage('Hej');
igSurf[0]:=idWallCon[i];
end
else
begin
if (Length(ConstructionName[i]) <>
Length(DerobModel.HouseProperties.StringValue['NWall'])) then
begin
ShowMessage('Length('+IntToStr(Length(ConstructionName[i]))+') <> Length('+
IntToStr(Length(DerobModel.HouseProperties.StringValue['NWall']))+')');
end
else
begin
for LCharPos := 1 to Length(ConstructionName[i]) do
begin
if (ConstructionName[i][LCharPos] <>
DerobModel.HouseProperties.StringValue['NWall'][LCharPos]) then
begin
//Here you might need to rather show the ordinal values of the
//characters to see the difference if they **look** the same due
//to the font of the message.
ShowMessage('Pos['+IntToStr(LCharPos)+'] "'+
ConstructionName[i][LCharPos]+'" <> "'+
DerobModel.HouseProperties.StringValue['NWall'][LCharPos]+'"');
end;
end;
end;
end;
The only thing I can think of that might unexpectedly cause "same" strings to be reported as different is: if they are different string types. E.g. if one is WideString and the other AnsiString, then:
There would have to be an implicit conversion to do the comparison.
And this means one of the strings would be changed.
The change could cause two strings that look the same to actually be different.

Any way to get TStringList.CommaText to not escape commas with quotes?

I'm doing some work with code generation, and one of the things I need to do is create a function call where one of the parameters is a function call, like so:
result := Func1(x, y, Func2(a, b, c));
TStringList.CommaText is very useful for generating the parameter lists, but when I traverse the tree to build the outer function call, what I end up with looks like this:
result := Func1(x, y, "Func2(a, b, c)");
It's quoting the third argument because it contains commas, and that produced invalid code. But I can't do something simplistic like StringReplace all double quotes with empty strings, because it's quite possible that a function argument could be a string with double quotes inside. Is there any way to make it just not escape the lines that contain commas?
You could set QuoteChar to be a space, and you'd merely get some extra spaces in the output, which is generally OK since generated code isn't usually expected to look pretty. String literals would be affected, though; they would have extra spaces inserted, changing the value of the string.
Free Pascal's TStrings class uses StrictDelimiter to control whether quoting occurs when reading the DelimitedText property. When it's true, quoting does not occur at all. Perhaps Delphi treats that property the same way.
Build an array of "unlikely characters" : non-keyable like †, ‡ or even non-printable like #129, #141, #143, #144.
Verify you don't have the 1st unlikely anywhere in your StringList.CommaText. Or move to the next unlikely until you get one not used in your StringList.CommaText. (Assert that you find one)
Use this unlikely char as the QuoteChar for your StringList
Get StringList.DelimitedText. You'll get the QuoteChar around the function parameters like: result := Func1(x, y, †Func2(a, b, c)†);
Replace the unlikely QuoteChar (here †) by empty strings...
What about using the Unicode version of AnsiExtractQuotedStr to remove the quotes?
Write your own method to export the contents of your TStringList to a string.
function MyStringListToString(const AStrings: TStrings): string;
var
i: Integer;
begin
Result := '';
if AStrings.Count = 0 then
Exit;
Result := AStrings[0];
for i := 1 to AStrings.Count - 1 do
Result := Result + ',' + AStrings[i];
end;
Too obvious? :-)
Alternatively, what would happen if you set StringList.QuoteChar to #0 and then called StringList.DelimitedText?
We have written a descendant class of TStringList in which reimplemented the DelimitedText property. You can copy most of the code from the original implementation.
var
LList: TStringList;
s, LOutput: string;
begin
LList := TStringList.Create;
try
LList.Add('x');
LList.Add('y');
LList.Add('Func2(a, b, c)');
for s in LList do
LOutput := LOutput + s + ', ';
SetLength(LOutput, Length(LOutput) - 2);
m1.AddLine('result := Func1(' + LOutput + ')');
finally
LList.Free;
end;
end;
Had the same problem, here's how I fixed it:
s := Trim(StringList.Text)
that's all ;-)

Resources