feat(rtl): WriteLn prints True/False for Boolean values
Add _SysWriteBool to the runtime platform layer so WriteLn(Boolean) prints 'True' or 'False' instead of '1' or '0', matching Delphi/FPC behaviour. Both QBE and native x86_64 backends emit the new call for tyBoolean arguments. Updated ~65 assertions across 13 test files.
This commit is contained in:
parent
0c5910195c
commit
d69fcacaca
|
|
@ -2753,8 +2753,9 @@ begin
|
|||
UInt32 / Word (unsigned-32) -> _SysWriteUInt64, zero-extended, so a
|
||||
value above 2^31 prints as a large
|
||||
positive number, not a signed wrap
|
||||
Boolean -> _SysWriteBool (prints True/False)
|
||||
everything else (Integer,
|
||||
Byte, Boolean, Enum, ...) -> _SysWriteInt (32-bit signed; their
|
||||
Byte, Enum, ...) -> _SysWriteInt (32-bit signed; their
|
||||
value range is non-negative there)
|
||||
The value is already in %rax 64-bit-extended (unsigned narrow loads
|
||||
zero-extend), so the unsigned-32 path needs no extra extension.
|
||||
|
|
@ -2785,6 +2786,13 @@ begin
|
|||
Self.Emit(#9'movl $1, %edi');
|
||||
Self.Emit(#9'callq _SysWriteSingle');
|
||||
end
|
||||
else if K = tyBoolean then
|
||||
begin
|
||||
Self.EmitExprToEax(ArgExpr);
|
||||
Self.Emit(#9'movl %eax, %esi');
|
||||
Self.Emit(#9'movl $1, %edi');
|
||||
Self.Emit(#9'callq _SysWriteBool');
|
||||
end
|
||||
else
|
||||
begin
|
||||
Self.EmitExprToEax(ArgExpr); { value -> %rax (64-bit-extended) }
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ unit uCodeGenQBE;
|
|||
|
||||
{ QBE IR emitter for Blaise.
|
||||
WriteLn/Write are built-ins emitted as calls to _SysWriteStr/_SysWriteInt/
|
||||
_SysWriteInt64/_SysWriteNewline (blaise_sys.pas / blaise_sys.c).
|
||||
_SysWriteInt64/_SysWriteBool/_SysWriteNewline (rtl.platform.posix.pas).
|
||||
Records are stack-allocated; field access uses pointer arithmetic. }
|
||||
|
||||
interface
|
||||
|
|
@ -7021,6 +7021,9 @@ begin
|
|||
ArgTemp := EmitExpr(ArgExpr);
|
||||
if IsString then
|
||||
EmitLine(Format(' call $_SysWriteStr(w %s, l %s)', [FdLit, ArgTemp]))
|
||||
else if (ArgExpr.ResolvedType <> nil) and
|
||||
(ArgExpr.ResolvedType.Kind = tyBoolean) then
|
||||
EmitLine(Format(' call $_SysWriteBool(w %s, w %s)', [FdLit, ArgTemp]))
|
||||
else if (ArgExpr.ResolvedType <> nil) and
|
||||
(ArgExpr.ResolvedType.Kind = tyDouble) then
|
||||
EmitLine(Format(' call $_SysWriteDouble(w %s, d %s)', [FdLit, ArgTemp]))
|
||||
|
|
|
|||
|
|
@ -565,7 +565,7 @@ var Output: string;
|
|||
begin
|
||||
Output := CompileAndRun(Src);
|
||||
if Output = '<toolchain-missing>' then begin Ignore('toolchain unavailable'); Exit end;
|
||||
AssertEquals('stdout', '1' + #10, Output);
|
||||
AssertEquals('stdout', 'True' + #10, Output);
|
||||
end;
|
||||
|
||||
procedure TCustomAttributeTests.TestE2E_HasClassAttribute_False;
|
||||
|
|
@ -584,7 +584,7 @@ var Output: string;
|
|||
begin
|
||||
Output := CompileAndRun(Src);
|
||||
if Output = '<toolchain-missing>' then begin Ignore('toolchain unavailable'); Exit end;
|
||||
AssertEquals('stdout', '0' + #10, Output);
|
||||
AssertEquals('stdout', 'False' + #10, Output);
|
||||
end;
|
||||
|
||||
procedure TCustomAttributeTests.TestE2E_HasClassAttribute_InheritedFromParent;
|
||||
|
|
@ -606,7 +606,7 @@ var Output: string;
|
|||
begin
|
||||
Output := CompileAndRun(Src);
|
||||
if Output = '<toolchain-missing>' then begin Ignore('toolchain unavailable'); Exit end;
|
||||
AssertEquals('stdout', '1' + #10 + '1' + #10, Output);
|
||||
AssertEquals('stdout', 'True' + #10 + 'True' + #10, Output);
|
||||
end;
|
||||
|
||||
procedure TCustomAttributeTests.TestE2E_HasClassAttribute_MultipleAttributes;
|
||||
|
|
@ -629,7 +629,7 @@ var Output: string;
|
|||
begin
|
||||
Output := CompileAndRun(Src);
|
||||
if Output = '<toolchain-missing>' then begin Ignore('toolchain unavailable'); Exit end;
|
||||
AssertEquals('stdout', '1' + #10 + '1' + #10, Output);
|
||||
AssertEquals('stdout', 'True' + #10 + 'True' + #10, Output);
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ type
|
|||
procedure TestWriteLn_NoArgs_CallsSysWriteNewline;
|
||||
procedure TestWriteLn_StringLit_CallsSysWriteStr;
|
||||
procedure TestWriteLn_IntExpr_CallsSysWriteInt;
|
||||
procedure TestWriteLn_BoolExpr_CallsSysWriteBool;
|
||||
|
||||
{ Variables and assignment }
|
||||
procedure TestIntVar_HasAlloc;
|
||||
|
|
@ -194,6 +195,15 @@ begin
|
|||
IRContains(IR, 'call $_SysWriteNewline(w 1)'));
|
||||
end;
|
||||
|
||||
procedure TCodeGenTests.TestWriteLn_BoolExpr_CallsSysWriteBool;
|
||||
var
|
||||
IR: string;
|
||||
begin
|
||||
IR := GenerateIR('program P; var b: Boolean; begin b := True; WriteLn(b) end.');
|
||||
AssertTrue('Calls _SysWriteBool',
|
||||
IRContains(IR, 'call $_SysWriteBool(w 1,'));
|
||||
end;
|
||||
|
||||
{ Variables and assignment }
|
||||
|
||||
procedure TCodeGenTests.TestIntVar_HasAlloc;
|
||||
|
|
|
|||
|
|
@ -849,16 +849,16 @@ begin
|
|||
'--- walk ---' + LE +
|
||||
' value=40' + LE +
|
||||
' tag=0' + LE +
|
||||
' marked=0' + LE +
|
||||
' marked=False' + LE +
|
||||
' value=30' + LE +
|
||||
' tag=1' + LE +
|
||||
' marked=1' + LE +
|
||||
' marked=True' + LE +
|
||||
' value=20' + LE +
|
||||
' tag=0' + LE +
|
||||
' marked=0' + LE +
|
||||
' marked=False' + LE +
|
||||
' value=10' + LE +
|
||||
' tag=0' + LE +
|
||||
' marked=0' + LE +
|
||||
' marked=False' + LE +
|
||||
'pop=40' + LE +
|
||||
'pop=30' + LE +
|
||||
'count_after_pops=2' + LE;
|
||||
|
|
@ -917,10 +917,10 @@ begin
|
|||
'list[1]_after_delete=30' + LE +
|
||||
'dict.count=4' + LE +
|
||||
'beta=2' + LE +
|
||||
'has_gamma=1' + LE +
|
||||
'has_gamma=True' + LE +
|
||||
'beta_after_update=99' + LE +
|
||||
'count_after_remove=3' + LE +
|
||||
'has_alpha_after_remove=0' + LE;
|
||||
'has_alpha_after_remove=False' + LE;
|
||||
AssertEquals('milestone stdout', Expected, Output);
|
||||
end;
|
||||
|
||||
|
|
@ -1194,7 +1194,7 @@ begin
|
|||
if not ToolchainAvailable then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertTrue('compile+run', CompileAndRun(SrcVarParamClassFields, Output, RCode));
|
||||
AssertEquals('exit code 0', 0, RCode);
|
||||
AssertEquals('var-param writeback visible through class field', '4096' + LE + '1' + LE, Output);
|
||||
AssertEquals('var-param writeback visible through class field', '4096' + LE + 'True' + LE, Output);
|
||||
end;
|
||||
|
||||
procedure TE2EClasses2Tests.TestRun_LocalVar_ShadowsOwnMethod;
|
||||
|
|
|
|||
|
|
@ -598,7 +598,7 @@ begin
|
|||
if not ToolchainAvailable then begin Fail('<toolchain-missing>'); Exit end;
|
||||
AssertTrue('compile+run', CompileAndRunWithRTL(SrcInstantNow, Output, RCode));
|
||||
AssertEquals('exit 0', 0, RCode);
|
||||
AssertEquals('InstantNow > 2020 epoch', '1', Trim(Output));
|
||||
AssertEquals('InstantNow > 2020 epoch', 'True', Trim(Output));
|
||||
end;
|
||||
|
||||
procedure TE2EDateUtilsTests.TestRun_MakeDate_ToString;
|
||||
|
|
@ -789,9 +789,9 @@ begin
|
|||
Lines := TStringList.Create;
|
||||
try
|
||||
Lines.Text := Trim(Output);
|
||||
AssertEquals('A.IsBefore(B)=true', '1', Lines.Strings[0]);
|
||||
AssertEquals('A.IsAfter(B)=false', '0', Lines.Strings[1]);
|
||||
AssertEquals('B.IsAfter(A)=true', '1', Lines.Strings[2]);
|
||||
AssertEquals('A.IsBefore(B)=true', 'True', Lines.Strings[0]);
|
||||
AssertEquals('A.IsAfter(B)=false', 'False', Lines.Strings[1]);
|
||||
AssertEquals('B.IsAfter(A)=true', 'True', Lines.Strings[2]);
|
||||
finally
|
||||
Lines.Free
|
||||
end
|
||||
|
|
@ -809,8 +809,8 @@ begin
|
|||
Lines := TStringList.Create;
|
||||
try
|
||||
Lines.Text := Trim(Output);
|
||||
AssertEquals('A.Equals(B)=true', '1', Lines.Strings[0]);
|
||||
AssertEquals('A.Equals(C)=false', '0', Lines.Strings[1]);
|
||||
AssertEquals('A.Equals(B)=true', 'True', Lines.Strings[0]);
|
||||
AssertEquals('A.Equals(C)=false', 'False', Lines.Strings[1]);
|
||||
finally
|
||||
Lines.Free
|
||||
end
|
||||
|
|
@ -909,8 +909,8 @@ begin
|
|||
Lines := TStringList.Create;
|
||||
try
|
||||
Lines.Text := Trim(Output);
|
||||
AssertEquals('IsNegative=true', '1', Lines.Strings[0]);
|
||||
AssertEquals('Abs.IsNegative=false', '0', Lines.Strings[1]);
|
||||
AssertEquals('IsNegative=true', 'True', Lines.Strings[0]);
|
||||
AssertEquals('Abs.IsNegative=false', 'False', Lines.Strings[1]);
|
||||
AssertEquals('Abs.TotalSeconds=120', '120', Lines.Strings[2]);
|
||||
finally
|
||||
Lines.Free
|
||||
|
|
@ -1035,7 +1035,7 @@ begin
|
|||
if not ToolchainAvailable then begin Fail('<toolchain-missing>'); Exit end;
|
||||
AssertTrue('compile+run', CompileAndRunWithRTL(SrcParseInstantOffset, Output, RCode));
|
||||
AssertEquals('exit 0', 0, RCode);
|
||||
AssertEquals('offset == UTC equivalent', '1', Trim(Output));
|
||||
AssertEquals('offset == UTC equivalent', 'True', Trim(Output));
|
||||
end;
|
||||
|
||||
procedure TE2EDateUtilsTests.TestRun_ParseDuration;
|
||||
|
|
@ -1091,10 +1091,10 @@ begin
|
|||
Lines := TStringList.Create;
|
||||
try
|
||||
Lines.Text := Trim(Output);
|
||||
AssertEquals('2000 is leap', '1', Lines.Strings[0]);
|
||||
AssertEquals('1900 not leap', '0', Lines.Strings[1]);
|
||||
AssertEquals('2024 is leap', '1', Lines.Strings[2]);
|
||||
AssertEquals('2025 not leap', '0', Lines.Strings[3]);
|
||||
AssertEquals('2000 is leap', 'True', Lines.Strings[0]);
|
||||
AssertEquals('1900 not leap', 'False', Lines.Strings[1]);
|
||||
AssertEquals('2024 is leap', 'True', Lines.Strings[2]);
|
||||
AssertEquals('2025 not leap', 'False', Lines.Strings[3]);
|
||||
finally
|
||||
Lines.Free
|
||||
end
|
||||
|
|
@ -1230,8 +1230,8 @@ begin
|
|||
Lines := TStringList.Create;
|
||||
try
|
||||
Lines.Text := Trim(Output);
|
||||
AssertEquals('>= -50400s', '1', Lines.Strings[0]);
|
||||
AssertEquals('<= +50400s', '1', Lines.Strings[1]);
|
||||
AssertEquals('>= -50400s', 'True', Lines.Strings[0]);
|
||||
AssertEquals('<= +50400s', 'True', Lines.Strings[1]);
|
||||
finally
|
||||
Lines.Free
|
||||
end
|
||||
|
|
|
|||
|
|
@ -342,10 +342,10 @@ begin
|
|||
if not ToolchainAvailable then begin Fail('<toolchain-missing>'); Exit end;
|
||||
AssertTrue('compile+run', CompileAndRun(SrcDictAddContains, Output, RCode));
|
||||
AssertEquals('exit 0', 0, RCode);
|
||||
{ ContainsKey(10) -> true (1), ContainsKey(99) -> false (0), Count=2 }
|
||||
AssertTrue('contains 10 -> 1', Pos('1', Output) >= 0);
|
||||
AssertTrue('contains 99 -> 0', Pos('0', Output) >= 0);
|
||||
AssertTrue('count=2', Pos('2', Output) >= 0);
|
||||
{ ContainsKey(10) -> true, ContainsKey(99) -> false, Count=2 }
|
||||
AssertTrue('contains 10 -> True', Pos('True', Output) >= 0);
|
||||
AssertTrue('contains 99 -> False', Pos('False', Output) >= 0);
|
||||
AssertTrue('count=2', Pos('2', Output) >= 0);
|
||||
end;
|
||||
|
||||
procedure TE2EIMapTests.TestRun_IMap_TDictionary_TryGetValue;
|
||||
|
|
@ -356,9 +356,9 @@ begin
|
|||
if not ToolchainAvailable then begin Fail('<toolchain-missing>'); Exit end;
|
||||
AssertTrue('compile+run', CompileAndRun(SrcDictTryGet, Output, RCode));
|
||||
AssertEquals('exit 0', 0, RCode);
|
||||
AssertTrue('found -> 1', Pos('1', Output) >= 0);
|
||||
AssertTrue('value=99', Pos('99', Output) >= 0);
|
||||
AssertTrue('missing -> 0', Pos('0', Output) >= 0);
|
||||
AssertTrue('found -> True', Pos('True', Output) >= 0);
|
||||
AssertTrue('value=99', Pos('99', Output) >= 0);
|
||||
AssertTrue('missing -> False', Pos('False', Output) >= 0);
|
||||
end;
|
||||
|
||||
procedure TE2EIMapTests.TestRun_IMap_TDictionary_Remove;
|
||||
|
|
@ -369,9 +369,9 @@ begin
|
|||
if not ToolchainAvailable then begin Fail('<toolchain-missing>'); Exit end;
|
||||
AssertTrue('compile+run', CompileAndRun(SrcDictRemove, Output, RCode));
|
||||
AssertEquals('exit 0', 0, RCode);
|
||||
AssertTrue('count=2', Pos('2', Output) >= 0);
|
||||
AssertTrue('removed key gone', Pos('0', Output) >= 0);
|
||||
AssertTrue('other key stays', Pos('1', Output) >= 0);
|
||||
AssertTrue('count=2', Pos('2', Output) >= 0);
|
||||
AssertTrue('removed key gone', Pos('False', Output) >= 0);
|
||||
AssertTrue('other key stays', Pos('True', Output) >= 0);
|
||||
end;
|
||||
|
||||
procedure TE2EIMapTests.TestRun_IMap_TOrderedDictionary_AddAndContainsKey;
|
||||
|
|
@ -382,9 +382,9 @@ begin
|
|||
if not ToolchainAvailable then begin Fail('<toolchain-missing>'); Exit end;
|
||||
AssertTrue('compile+run', CompileAndRun(SrcOrdDictAddContains, Output, RCode));
|
||||
AssertEquals('exit 0', 0, RCode);
|
||||
AssertTrue('contains 10 -> 1', Pos('1', Output) >= 0);
|
||||
AssertTrue('contains 99 -> 0', Pos('0', Output) >= 0);
|
||||
AssertTrue('count=2', Pos('2', Output) >= 0);
|
||||
AssertTrue('contains 10 -> True', Pos('True', Output) >= 0);
|
||||
AssertTrue('contains 99 -> False', Pos('False', Output) >= 0);
|
||||
AssertTrue('count=2', Pos('2', Output) >= 0);
|
||||
end;
|
||||
|
||||
procedure TE2EIMapTests.TestRun_IMap_TOrderedDictionary_TryGetValue;
|
||||
|
|
@ -395,9 +395,9 @@ begin
|
|||
if not ToolchainAvailable then begin Fail('<toolchain-missing>'); Exit end;
|
||||
AssertTrue('compile+run', CompileAndRun(SrcOrdDictTryGet, Output, RCode));
|
||||
AssertEquals('exit 0', 0, RCode);
|
||||
AssertTrue('found -> 1', Pos('1', Output) >= 0);
|
||||
AssertTrue('value=99', Pos('99', Output) >= 0);
|
||||
AssertTrue('missing -> 0', Pos('0', Output) >= 0);
|
||||
AssertTrue('found -> True', Pos('True', Output) >= 0);
|
||||
AssertTrue('value=99', Pos('99', Output) >= 0);
|
||||
AssertTrue('missing -> False', Pos('False', Output) >= 0);
|
||||
end;
|
||||
|
||||
procedure TE2EIMapTests.TestRun_IMap_SwapImplementation_SameCallSite;
|
||||
|
|
@ -408,8 +408,8 @@ begin
|
|||
if not ToolchainAvailable then begin Fail('<toolchain-missing>'); Exit end;
|
||||
AssertTrue('compile+run', CompileAndRun(SrcSwapImpl, Output, RCode));
|
||||
AssertEquals('exit 0', 0, RCode);
|
||||
AssertTrue('TDict dispatch correct', Pos('1', Output) >= 0);
|
||||
AssertTrue('TOrdDict dispatch correct', Pos('1', Output) >= 0);
|
||||
AssertTrue('TDict dispatch correct', Pos('True', Output) >= 0);
|
||||
AssertTrue('TOrdDict dispatch correct', Pos('True', Output) >= 0);
|
||||
end;
|
||||
|
||||
{ Regression: a generic class implementing a generic interface
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ type
|
|||
published
|
||||
{ Boolean, WriteLn, break/exit }
|
||||
procedure TestRun_BooleanOps_AllExpressions;
|
||||
procedure TestRun_WriteLn_BoolVar_PrintsTrueOrFalse;
|
||||
procedure TestRun_WriteLn_BoolExpr_PrintsTrueOrFalse;
|
||||
procedure TestRun_MultiArgWriteLn_PrintsAllArgs;
|
||||
procedure TestRun_ForBreak_StopsAtFiveHalt;
|
||||
procedure TestRun_ExitFromFunction_ReturnsImmediately;
|
||||
|
|
@ -102,6 +104,25 @@ const
|
|||
end.
|
||||
''';
|
||||
|
||||
SrcWriteLnBoolVar = '''
|
||||
program P;
|
||||
var B: Boolean;
|
||||
begin
|
||||
B := True;
|
||||
WriteLn(B);
|
||||
B := False;
|
||||
WriteLn(B)
|
||||
end.
|
||||
''';
|
||||
|
||||
SrcWriteLnBoolExpr = '''
|
||||
program P;
|
||||
begin
|
||||
WriteLn(3 > 2);
|
||||
WriteLn(1 = 2)
|
||||
end.
|
||||
''';
|
||||
|
||||
SrcMultiArg = '''
|
||||
program P;
|
||||
var I, J, K: Integer;
|
||||
|
|
@ -522,6 +543,18 @@ begin
|
|||
't1' + LE + 't2' + LE + 't3' + LE, Output);
|
||||
end;
|
||||
|
||||
procedure TE2EMiscTests.TestRun_WriteLn_BoolVar_PrintsTrueOrFalse;
|
||||
begin
|
||||
if not ToolchainAvailable then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertRunsOnBoth(SrcWriteLnBoolVar, 'True' + LE + 'False' + LE, 0);
|
||||
end;
|
||||
|
||||
procedure TE2EMiscTests.TestRun_WriteLn_BoolExpr_PrintsTrueOrFalse;
|
||||
begin
|
||||
if not ToolchainAvailable then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertRunsOnBoth(SrcWriteLnBoolExpr, 'True' + LE + 'False' + LE, 0);
|
||||
end;
|
||||
|
||||
procedure TE2EMiscTests.TestRun_MultiArgWriteLn_PrintsAllArgs;
|
||||
var Output: string; RCode: Integer;
|
||||
begin
|
||||
|
|
|
|||
|
|
@ -1536,7 +1536,7 @@ const
|
|||
procedure TE2ENativeTests.TestRun_Native_String_SameText;
|
||||
begin
|
||||
if not ToolchainAvailable then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertRunsOnBoth(SrcStrSameText, '1' + LE, 0);
|
||||
AssertRunsOnBoth(SrcStrSameText, 'True' + LE, 0);
|
||||
end;
|
||||
|
||||
procedure TE2ENativeTests.TestRun_Native_String_Format_IntArg;
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ var Output: string; RCode: Integer;
|
|||
begin
|
||||
if not ToolchainAvailable then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertTrue('compile+run', CompileAndRun(SrcStringSameText, Output, RCode));
|
||||
AssertEquals('SameText(''Hello'', ''hello'') = True (1)', '1', Trim(Output));
|
||||
AssertEquals('SameText(''Hello'', ''hello'') = True', 'True', Trim(Output));
|
||||
end;
|
||||
|
||||
procedure TE2EStringOpsTests.TestRun_StringOps_IntToStr;
|
||||
|
|
|
|||
|
|
@ -292,8 +292,8 @@ begin
|
|||
Lines := TStringList.Create;
|
||||
try
|
||||
Lines.Text := Trim(Output);
|
||||
AssertEquals('existing file = 1', '1', Lines.Strings[0]);
|
||||
AssertEquals('missing file = 0', '0', Lines.Strings[1]);
|
||||
AssertEquals('existing file = True', 'True', Lines.Strings[0]);
|
||||
AssertEquals('missing file = False', 'False', Lines.Strings[1]);
|
||||
finally
|
||||
Lines.Free;
|
||||
end;
|
||||
|
|
@ -401,8 +401,8 @@ begin
|
|||
Lines := TStringList.Create;
|
||||
try
|
||||
Lines.Text := Trim(Output);
|
||||
AssertEquals('/tmp exists = 1', '1', Lines.Strings[0]);
|
||||
AssertEquals('missing dir = 0', '0', Lines.Strings[1]);
|
||||
AssertEquals('/tmp exists = True', 'True', Lines.Strings[0]);
|
||||
AssertEquals('missing dir = False', 'False', Lines.Strings[1]);
|
||||
finally
|
||||
Lines.Free;
|
||||
end;
|
||||
|
|
@ -436,8 +436,8 @@ begin
|
|||
Lines := TStringList.Create;
|
||||
try
|
||||
Lines.Text := Trim(Output);
|
||||
AssertEquals('ForceDirectories returned 1', '1', Lines.Strings[0]);
|
||||
AssertEquals('DirectoryExists returned 1', '1', Lines.Strings[1]);
|
||||
AssertEquals('ForceDirectories returned True', 'True', Lines.Strings[0]);
|
||||
AssertEquals('DirectoryExists returned True', 'True', Lines.Strings[1]);
|
||||
finally
|
||||
Lines.Free;
|
||||
end;
|
||||
|
|
@ -568,9 +568,9 @@ begin
|
|||
Lines := TStringList.Create;
|
||||
try
|
||||
Lines.Text := Trim(Output);
|
||||
AssertEquals('RenameFile returns 1', '1', Lines.Strings[0]);
|
||||
AssertEquals('old name gone (0)', '0', Lines.Strings[1]);
|
||||
AssertEquals('new name exists (1)', '1', Lines.Strings[2]);
|
||||
AssertEquals('RenameFile returns True', 'True', Lines.Strings[0]);
|
||||
AssertEquals('old name gone (False)', 'False', Lines.Strings[1]);
|
||||
AssertEquals('new name exists (True)', 'True', Lines.Strings[2]);
|
||||
finally
|
||||
Lines.Free;
|
||||
end;
|
||||
|
|
@ -586,7 +586,7 @@ begin
|
|||
if not ToolchainAvailable then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertTrue('compile+run', CompileAndRun(SrcRenameFileMissing, Output, RCode));
|
||||
AssertEquals('exit code 0', 0, RCode);
|
||||
AssertEquals('RenameFile missing = 0', '0', Trim(Output));
|
||||
AssertEquals('RenameFile missing = False', 'False', Trim(Output));
|
||||
end;
|
||||
|
||||
procedure TE2ESysUtilsTests.TestRun_SetCurrentDir_ChangesDir;
|
||||
|
|
@ -601,8 +601,8 @@ begin
|
|||
Lines := TStringList.Create;
|
||||
try
|
||||
Lines.Text := Trim(Output);
|
||||
AssertEquals('SetCurrentDir /tmp returns 1', '1', Lines.Strings[0]);
|
||||
AssertEquals('GetCurrentDir non-empty', '1', Lines.Strings[1]);
|
||||
AssertEquals('SetCurrentDir /tmp returns True', 'True', Lines.Strings[0]);
|
||||
AssertEquals('GetCurrentDir non-empty', 'True', Lines.Strings[1]);
|
||||
finally
|
||||
Lines.Free;
|
||||
end;
|
||||
|
|
@ -659,9 +659,9 @@ begin
|
|||
Lines := TStringList.Create;
|
||||
try
|
||||
Lines.Text := Trim(Output);
|
||||
AssertEquals('LineEnding = #10', '1', Lines.Strings[0]);
|
||||
AssertEquals('DirectorySeparator = /', '1', Lines.Strings[1]);
|
||||
AssertEquals('PathSeparator = :', '1', Lines.Strings[2]);
|
||||
AssertEquals('LineEnding = #10', 'True', Lines.Strings[0]);
|
||||
AssertEquals('DirectorySeparator = /', 'True', Lines.Strings[1]);
|
||||
AssertEquals('PathSeparator = :', 'True', Lines.Strings[2]);
|
||||
finally
|
||||
Lines.Free;
|
||||
end;
|
||||
|
|
@ -673,7 +673,7 @@ begin
|
|||
if not ToolchainAvailable then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertTrue('compile+run', CompileAndRun(SrcGetCurrentDir, Output, RCode));
|
||||
AssertEquals('exit code 0', 0, RCode);
|
||||
AssertEquals('GetCurrentDir non-empty', '1', Trim(Output));
|
||||
AssertEquals('GetCurrentDir non-empty', 'True', Trim(Output));
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ begin
|
|||
Lines := TStringList.Create;
|
||||
try
|
||||
Lines.Text := Trim(Output);
|
||||
AssertEquals('Owner=nil is true', '1', Lines.Strings[0]);
|
||||
AssertEquals('Owner=nil is true', 'True', Lines.Strings[0]);
|
||||
AssertEquals('ComponentCount=0', '0', Lines.Strings[1]);
|
||||
finally
|
||||
Lines.Free
|
||||
|
|
@ -173,7 +173,7 @@ begin
|
|||
if not ToolchainAvailable then begin Fail('<toolchain-missing>'); Exit end;
|
||||
AssertTrue('compile+run', CompileAndRunWithRTL(SrcOwnerRef, Output, RCode));
|
||||
AssertEquals('exit 0', 0, RCode);
|
||||
AssertEquals('Child.Owner=Parent', '1', Trim(Output));
|
||||
AssertEquals('Child.Owner=Parent', 'True', Trim(Output));
|
||||
end;
|
||||
|
||||
procedure TE2ETComponentTests.TestRun_ComponentCount;
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ begin
|
|||
if not ToolchainAvailable then begin Ignore('toolchain unavailable'); Exit end;
|
||||
AssertTrue('compile+run', CompileAndRunWithRTL(SrcTerminateFlag, Output, RCode));
|
||||
AssertEquals('exit code', 0, RCode);
|
||||
AssertEquals('stdout', '1' + LE + 'ok' + LE, Output)
|
||||
AssertEquals('stdout', 'True' + LE + 'ok' + LE, Output)
|
||||
end;
|
||||
|
||||
procedure TE2EThreadingTests.TestRun_Thread_FinishedAfterExecute;
|
||||
|
|
@ -284,7 +284,7 @@ begin
|
|||
if not ToolchainAvailable then begin Ignore('toolchain unavailable'); Exit end;
|
||||
AssertTrue('compile+run', CompileAndRunWithRTL(SrcFinishedFlag, Output, RCode));
|
||||
AssertEquals('exit code', 0, RCode);
|
||||
AssertEquals('stdout', '0' + LE + '1' + LE, Output)
|
||||
AssertEquals('stdout', 'False' + LE + 'True' + LE, Output)
|
||||
end;
|
||||
|
||||
procedure TE2EThreadingTests.TestRun_Thread_MultipleThreads;
|
||||
|
|
|
|||
|
|
@ -215,10 +215,10 @@ begin
|
|||
if not ToolchainAvailable then begin Fail('<toolchain-missing>'); Exit end;
|
||||
AssertTrue('compile+run', CompileAndRun(SrcTryGetValue, Output, RCode));
|
||||
AssertEquals('exit 0', 0, RCode);
|
||||
{ Boolean true prints as 1, false as 0 in Blaise }
|
||||
AssertTrue('found -> true (1)', Pos('1', Output) >= 0);
|
||||
AssertTrue('value=99', Pos('99', Output) >= 0);
|
||||
AssertTrue('missing -> false (0)', Pos('0', Output) >= 0);
|
||||
{ Boolean true prints as True, false as False in Blaise }
|
||||
AssertTrue('found -> true (True)', Pos('True', Output) >= 0);
|
||||
AssertTrue('value=99', Pos('99', Output) >= 0);
|
||||
AssertTrue('missing -> false (False)', Pos('False', Output) >= 0);
|
||||
end;
|
||||
|
||||
procedure TE2EOrdDictTests.TestRun_OrdDict_Remove;
|
||||
|
|
|
|||
|
|
@ -176,8 +176,8 @@ begin
|
|||
AssertTrue('compile+run', CompileAndRun(SrcExclude, Output, RCode));
|
||||
AssertEquals('exit 0', 0, RCode);
|
||||
AssertTrue('count=2 after exclude', Pos('2', Output) >= 0);
|
||||
{ Boolean false prints as 0 in Blaise }
|
||||
AssertTrue('contains returns false (0)', Pos('0', Output) >= 0);
|
||||
{ Boolean false prints as False in Blaise }
|
||||
AssertTrue('contains returns false (False)', Pos('False', Output) >= 0);
|
||||
end;
|
||||
|
||||
procedure TE2ESetTests.TestRun_TSet_ContainsMembership;
|
||||
|
|
@ -188,9 +188,9 @@ begin
|
|||
if not ToolchainAvailable then begin Fail('<toolchain-missing>'); Exit end;
|
||||
AssertTrue('compile+run', CompileAndRun(SrcContains, Output, RCode));
|
||||
AssertEquals('exit 0', 0, RCode);
|
||||
{ Boolean true prints as 1, false as 0 in Blaise }
|
||||
AssertTrue('contains 42 -> true (1)', Pos('1', Output) >= 0);
|
||||
AssertTrue('contains 99 -> false (0)', Pos('0', Output) >= 0);
|
||||
{ Boolean true prints as True, false as False in Blaise }
|
||||
AssertTrue('contains 42 -> true (True)', Pos('True', Output) >= 0);
|
||||
AssertTrue('contains 99 -> false (False)', Pos('False', Output) >= 0);
|
||||
end;
|
||||
|
||||
procedure TE2ESetTests.TestRun_TSet_CountTracking;
|
||||
|
|
|
|||
|
|
@ -669,8 +669,8 @@ begin
|
|||
try
|
||||
Lines.Text := Trim(Output);
|
||||
AssertEquals('Count=2', '2', Lines.Strings[0]);
|
||||
AssertEquals('Text contains line1', '1', Lines.Strings[1]);
|
||||
AssertEquals('Text contains line2', '1', Lines.Strings[2]);
|
||||
AssertEquals('Text contains line1', 'True', Lines.Strings[1]);
|
||||
AssertEquals('Text contains line2', 'True', Lines.Strings[2]);
|
||||
finally
|
||||
Lines.Free
|
||||
end
|
||||
|
|
@ -692,9 +692,9 @@ begin
|
|||
AssertEquals('Count=500', '500', Lines.Strings[0]);
|
||||
LenStr := Lines.Strings[1];
|
||||
AssertTrue('Length(Text) > 3500', StrToInt(LenStr) > 3500);
|
||||
AssertEquals('contains line_1', '1', Lines.Strings[2]);
|
||||
AssertEquals('contains line_250', '1', Lines.Strings[3]);
|
||||
AssertEquals('contains line_499', '1', Lines.Strings[4]);
|
||||
AssertEquals('contains line_1', 'True', Lines.Strings[2]);
|
||||
AssertEquals('contains line_250', 'True', Lines.Strings[3]);
|
||||
AssertEquals('contains line_499', 'True', Lines.Strings[4]);
|
||||
finally
|
||||
Lines.Free
|
||||
end
|
||||
|
|
@ -788,9 +788,9 @@ begin
|
|||
Lines := TStringList.Create;
|
||||
try
|
||||
Lines.Text := Trim(Output);
|
||||
AssertEquals('Find(cherry)=1 (true)', '1', Lines.Strings[0]);
|
||||
AssertEquals('Find(cherry) idx', '1', Lines.Strings[1]);
|
||||
AssertEquals('Find(durian)=0 (false)', '0', Lines.Strings[2]);
|
||||
AssertEquals('Find(cherry)=True (true)', 'True', Lines.Strings[0]);
|
||||
AssertEquals('Find(cherry) idx', '1', Lines.Strings[1]);
|
||||
AssertEquals('Find(durian)=False (false)', 'False', Lines.Strings[2]);
|
||||
finally
|
||||
Lines.Free
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3172,3 +3172,24 @@ All three constants are defined in `system.pas` and pre-seeded as compiler
|
|||
built-ins in `RegisterBuiltins`. Their values are derived from the compilation
|
||||
target (`GTarget` in `blaise.codegen.target`), so cross-compilation to a
|
||||
different OS produces the correct constants without conditional compilation.
|
||||
|
||||
== Boolean formatting — `WriteLn` prints `True` / `False`
|
||||
|
||||
Both Delphi and FPC print `TRUE` / `FALSE` (case varies by RTL version) when a
|
||||
Boolean value is passed to `Write` / `WriteLn`. Early Blaise treated booleans as
|
||||
plain integers, so `WriteLn(True)` printed `1` and `WriteLn(False)` printed `0`.
|
||||
|
||||
This was a codegen shortcut, not a deliberate design choice. It surprised users
|
||||
familiar with any other Pascal dialect and made program output harder to read.
|
||||
|
||||
Blaise now emits a dedicated `_SysWriteBool` call for Boolean-typed arguments.
|
||||
The runtime writes the literal string `True` or `False` — no heap allocation, no
|
||||
string conversion. `BoolToStr` in `sysutils.pas` is unaffected and continues to
|
||||
return `'True'` / `'False'` as a string value.
|
||||
|
||||
*Alternatives considered:*
|
||||
|
||||
* `TRUE` / `FALSE` (all-caps, matching Delphi) — rejected; Blaise identifiers are
|
||||
`True` / `False` (title-case), and the output should match the language.
|
||||
* Keeping `1` / `0` and requiring explicit `BoolToStr` — rejected; the implicit
|
||||
integer rendering was a bug, not a feature.
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ type
|
|||
procedure SysWriteUInt64(Fd: Integer; N: UInt64); virtual; abstract;
|
||||
procedure SysWriteDouble(Fd: Integer; V: Double); virtual; abstract;
|
||||
procedure SysWriteSingle(Fd: Integer; V: Single); virtual; abstract;
|
||||
procedure SysWriteBool(Fd: Integer; B: Boolean); virtual; abstract;
|
||||
procedure SysWriteNewline(Fd: Integer); virtual; abstract;
|
||||
|
||||
{ File-descriptor primitives — used by streams }
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ type
|
|||
procedure SysWriteUInt64(Fd: Integer; N: UInt64); override;
|
||||
procedure SysWriteDouble(Fd: Integer; V: Double); override;
|
||||
procedure SysWriteSingle(Fd: Integer; V: Single); override;
|
||||
procedure SysWriteBool(Fd: Integer; B: Boolean); override;
|
||||
procedure SysWriteNewline(Fd: Integer); override;
|
||||
|
||||
{ File-descriptor primitives }
|
||||
|
|
@ -243,6 +244,7 @@ procedure _SysWriteInt64(Fd: Integer; N: Int64);
|
|||
procedure _SysWriteUInt64(Fd: Integer; N: UInt64);
|
||||
procedure _SysWriteDouble(Fd: Integer; V: Double);
|
||||
procedure _SysWriteSingle(Fd: Integer; V: Single);
|
||||
procedure _SysWriteBool(Fd: Integer; B: Boolean);
|
||||
procedure _SysWriteNewline(Fd: Integer);
|
||||
|
||||
{ File-descriptor primitives }
|
||||
|
|
@ -802,6 +804,22 @@ begin
|
|||
_StringRelease(S);
|
||||
end;
|
||||
|
||||
procedure TRtlPlatformPosix.SysWriteBool(Fd: Integer; B: Boolean);
|
||||
var
|
||||
Buf: array[0..4] of Byte;
|
||||
begin
|
||||
if B then
|
||||
begin
|
||||
Buf[0] := 84; Buf[1] := 114; Buf[2] := 117; Buf[3] := 101;
|
||||
WriteAllToFd(Fd, PChar(@Buf[0]), 4);
|
||||
end
|
||||
else
|
||||
begin
|
||||
Buf[0] := 70; Buf[1] := 97; Buf[2] := 108; Buf[3] := 115; Buf[4] := 101;
|
||||
WriteAllToFd(Fd, PChar(@Buf[0]), 5);
|
||||
end;
|
||||
end;
|
||||
|
||||
{ ================================================================== }
|
||||
{ TRtlPlatformPosix — File-descriptor primitives }
|
||||
{ ================================================================== }
|
||||
|
|
@ -1277,6 +1295,11 @@ begin
|
|||
GRtlPlatform.SysWriteSingle(Fd, V);
|
||||
end;
|
||||
|
||||
procedure _SysWriteBool(Fd: Integer; B: Boolean);
|
||||
begin
|
||||
GRtlPlatform.SysWriteBool(Fd, B);
|
||||
end;
|
||||
|
||||
procedure _SysWriteNewline(Fd: Integer);
|
||||
begin
|
||||
GRtlPlatform.SysWriteNewline(Fd);
|
||||
|
|
|
|||
Loading…
Reference in a new issue