Allowing types to have attributes e.g. "is trimmed on assignment" would probably fix this, but unless the language were completely reengineered to be extensible they'd have to be agreed on in advance.
Yeah, I mean compiler magic is always a solution that could be implemented, but I think such things are way to specific to justify having custom compiler makros or so for them. A more general approach, used in other languages like Java, is the defining of custom annotations that influence the code generation during compiletime. E.g.
From the Lombok package automatically generates setMyString and getMyString methods. These can be defined in Java, which is executed during compiletime.
Something like this could allow for defining custom attributes that will be executed during compiletime.
Another option would be compiletime functions. C++ allows for constexpr functions, these are functions of whoom all inputs are constants available during compiletime, that will be evaluated during compiletime. A constexpr TrimIndent could there be implemented that way.
But to be honest, Pascal is probably not the kind of language where such features are going to be implemented. While they can be very neat with regards to usability (like the getter and setter example from above), they add a whole new dimension to the language, where the behavior of every line of code can be completely altered with such annotations, which need to be understood by the maintainer in the first place (and looking at some enterprise Java source, these annotations can look like black magic).
Something that might be more interesting would be if runtime code could be used in the definition segment. I think the ugliest part of pascal that always create really bad code is the use of the initialization and finalization segment.
Very often I use this segment to initialize global variables, like loggers or something like this. This means the logger is defined somewhere in the middle of the file, but the initialization code is at the very bottom, often like 500 lines or more away from the definition of the data the code relates to.
I think there could be a usefull alternative, something like:
var
MyLogger: TLogger initialize MyLogger := TLogger.Create(Output) finalize MyLogger.Free;
with a multiline alternative:
var
MyLogger: TLogger initialize
begin
SetConsoleOutputCP(DefaultSystemCodePage);
SetTextCodePage(Output, DefaultSystemCodePage);
MyLogger := TLogger.Create(Output);
end
finalize
MyLogger.Free;
In that case the trimming could be done with something like that:
var
mls: String initialize mls := 'foo '.Trim;
But this is just an idea I had some time ago because I really hate the way the initialization and finalization segments work currently.
PS: if we only consider runtime code, one way to handle something like this would be a wrapper type:
{ TTrimmedString }
TTrimmedString = record
private
Data: String;
public
class operator :=(const src: String): TTrimmedString;
class operator :=(const src: TTrimmedString): String;
end;
{ TTrimmedString }
class operator TTrimmedString.:=(const src: String): TTrimmedString;
begin
Result.Data:=src.Trim;
end;
class operator TTrimmedString.:=(const src: TTrimmedString): String;
begin
Result := src.Data;
end;
var
s: TTrimmedString;
begin
s := '42 ';
Write(StrToInt(s)); // can be used for any string implicetly
ReadLn;
end.
But again this is restricted to runtime code only