The DLangExtensions adds new language constructs to the ObjectPascal language. This is achieved by using a preprocessor for the IDE and the command line compiler.
WARNING: No official stable version is released. The development snapshot builds are work in
progress and may not be stable.
Use at your own risk.
At the moment only development snapshot builds are available.
This extension supports some file information macros.
Macro Datatype Description $__FILE__ string full qualified filename of the current file $__PROJECT__ string name of the project (dpr/dpk) without path and without suffix $__LINE__ Integer current source code line $__TIME__ TDateTime compilation timestamp
Example:
procedure TFormMain.BtnDemoClick(Sender: TObject);
begin
ShowMessage('Filename: ' + $__FILE__);
ShowMessage('Project: ' + $__PROJECT__);
ShowMessage('Line: ' + IntToStr($__LINE__));
ShowMessage('Time: ' + DateTimeToStr($__TIME__));
end;
The case-string-of extension introduces a case-of that uses strings instead of ordinal-values.
Syntax case string(string-expression) of
string-literal/string-constant [, string-literal/string-constant ...]: statement;
...
[else
statements]
end;The case-string-of extension automatically generates an include file. This file is included immediatelly after the implementation keyword or after the implementation's uses list. But it is possible to move the automatically inserted $INCLUDE by using the "{$INCLUDE CASE-STRING-OF}" compiler directive. The "{$INCLUDE CASE-STRING-OF}" may be also necessary if the implementation's uses list contains a complicated $IFDEF/$IF condition.
You can also use case AnsiString(expression) of or case WideString(expression) of to reduce string conversion.
Limitations
Only literal strings and contants are allowed.- Constants or string concatenations make the case-string-of slower because no hash table can be used.
- The comparision is case sensitive.
Example:
procedure TFormExecCmd.btnExecuteClick(Sender: TObject); begin case string(edtCommand.Text)of 'Hallo', 'Hello': ShowMessage('Hallo'); 'Exit': Close; else ShowMessage('Unknown command: ' + edtCommand.Text); end; end;
The extended for-in loop allows you to iterator over an array or every object that has a Count property and a default array-property. Furthermore it can be used with classes that have a GetEnumerator method. The for-in iterator variable's scope is limited to the for-in loop.
Syntax Description for I: DataType in List[] do ; List is an instance of a class that has a Count property and a default array-property (like TList, TStrings).
This is not type safe because it uses a hard typecast to DataTypefor object C: ClassType in List[] do ; List is an instance of a class that has a Count property and a default array-property (like TList, TStrings).
This is type safe because it uses the as-operator for the typecast to ClassTypefor I: DataType in array List do ; List is a static/dynamic array or a string.
This is not type safe because it uses a hard typecast to DataTypefor object C: ClassType in array List do ; List is a static/dynamic array or a string.
This is type safe because it uses the as-operator for the typecast to ClassTypefor I: DataType in List do ; List is an instance of a class that has a GetEnumerator method.
This is not type safe because it uses a hard typecast to DataTypefor object C: ClassType in List do ; List is an instance of a class that has a GetEnumerator method.
This is type safe because it uses the as-operator for the typecast to ClassTypeThe for-in extension automatically generates an include file. This file is included immediatelly after the implementation keyword or after the implementation's uses list. This restricts the usage of the iterator-types to those that are declared in the interface block or in the imported units. But it is possible to move the automatically inserted $INCLUDE by using the "{$INCLUDE FOR-IN}" compiler directive. The "{$INCLUDE FOR-IN}" may be also necessary if the implementation's uses list contains a complicated $IFDEF/$IF condition.
GetEnumerator
The GetEnumerator method must return a new instance of a class that has the following methods:
Enumerator methods function GetCurrent: DataType; function MoveNext: Boolean;
Limitations
- The "for [object] It: DataType in List[]" and the "for [object] It: DataType in array List" loops access the List very often. So the List should be a variable an not a function call. This does not apply to the GetEnumerator for-in loops.
- If the DataType is an interface, you should set the iterator variable to NIL after each iteration. Otherwise you wouldn't be able to clear the interface after the for-in loop has finished because the iterator variable isn't visible anymore. The iterator variable will be always cleared when the surrounding function is left.
Example:
var List: TList; Arr: array[0..1] of AnsiChar; begin Arr[0] := 'a'; Arr[1] := 'z'; for Value: Byte in array Arr do // hard typecast to Byte Memo1.Lines.Add(IntToStr(Value)); List := TList.Create; try List.Add(Pointer(1)); List.Add(Pointer(2)); List.Add(Pointer(4)); List.Add(Pointer(8)); List.Add(Pointer(16)); for Value: Integer in List[] do // hard typecast to Integer Memo1.Lines.Add(IntToStr(Value)); finally List.Free; end; end;
var List: TObjectList; I: Integer; begin List := TObjectList.Create(False); try for I := 0 to ComponentCount - 1 do List.Add(Components[I]); for object c: TComponent in List[] do // type safe (uses as-operator) begin Memo1.Lines.Add(c.Name); for c: TComponent in List[] do // hard typecast to TComponent Memo1.Lines.Add(c.Name); end; finally List.Free; end; end;
type TObjectListEnumerator = class private FIndex: Integer; FList: TObjectList; public constructor Create(AList: TObjectList); function GetCurrent: TObject; function MoveNext: Boolean; property Current: TObject read GetCurrent; end; TObjectListEx = class(TObjectList) public function GetEnumerator: TObjectListEnumerator; end; { TObjectListEnumerator } constructor TObjectListEnumerator.Create(AList: TObjectList); begin inherited Create; FIndex := -1; FList := AList; end; function TObjectListEnumerator.GetCurrent: TObject; begin Result := FList[FIndex]; end; function TObjectListEnumerator.MoveNext: Boolean; begin Result := FIndex < FList.Count - 1; if Result then Inc(FIndex); end; { TObjectListEx } function TObjectListEx.GetEnumerator: TObjectListEnumerator; begin Result := TObjectListEnumerator.Create(Self); end; var ObjList: TObjectListEx; I: Integer; begin ObjList := TObjectListEx.Create(False); try for I := 0 to ComponentCount - 1 do ObjList.Add(Components[I]); for object c: TComponent in ObjList do Memo1.Lines.Add(c.Name); finally ObjList.Free; end; end;
A multiline string is started by "<<" followed (without a space) by a user defined terminator token which must not contain a space and is ended by a line break or a single line comment. The next line is the first multiline string line. A multiline string is terminated by a new line that starts with the terminator token. If this token is indented, the indention is removed from all multiline string lines. This makes it possible to indent the whole string without having the indention in the resulting string. The "#<code>" token has a special meaning in the multiline string. It inlines source code into the string what is technically just a string concatination, so the lvalue of the expression must be a string. A "#" char must be escaped by prepanding an additional "#". Otherwise it would indicate code inlining.
Examples:
S := <<MARKER
multi line
string
MARKER;
ShowMessage(<<@@
multi line
string
@@); // this removes the two space indention from the multi line string
S := <<@@ // comments are allowed in this line
##1. i = #< IntToStr(i) >
##2. Value = #< VarToStr(Value) >
@@;
The above examples are expanded to the following code:
S :=
'multi line'#13#10+
'string'
;
ShowMessage(
'multi line'#13#10+
'string'
); // this removes the two space indention from the multi line string
S := // comments are allowed in this line
'#1. i = ' + IntToStr(i) + ''#13#10+
'#2. Value = ' + VarToStr(Value) + ''#13#10
;
The Unicode Switch changes the meaning of "string", "Char" and "PChar" to "RawByteString", "AnsiChar" and "PAnsiChar". It also typecasts all constant strings and constant characters to "RawByteString" and "AnsiChar". This reduces the number of implicit typecasts by the compiler and the ANSI functions are called and not the Unicode versions that the compiler prefers.
Enabling/Disabling the ANSI mode
The ANSI mode is activated by the new ANSISTRINGS switch.
{%ANSISTRINGS ON/ACTIVE/OFF}The switch uses the %-character instead of the $-character because otherwise Error Insight will show you errors.
Switch Description ON enables AnsiStrings, undefines UNICODE, adds alias types for Code Insight ACTIVE enables AnsiStrings, undefines UNICODE (CodeInsight and Compiler column-info can be wrong) OFF disables AnsiStrings, defines UNICODE
Limitations
- The {%ANSISTRINGS ON} must be inserted below the "uses" clause in the "interface" or "implementation" block. Thus it can't be used between "begin" and "end". This is because the switch must declare type aliases to keep the column information correct
{%ANSISTRINGS ACTIVE} doesn't have this limitation.- The switch cannot be IFDEFed. IFDEFs are ignored.
- The switch does not "ansify" your code it just changes the meaning of "string", "Char" and "PChar" to "RawByteString", "AnsiChar" and "PAnsiChar". Nothing more, nothing less.