Author Topic: Unleashed Pascal (async/await, parallel for, match, string interpolation & more)  (Read 63674 times)

flowCRANE

  • Hero Member
  • *****
  • Posts: 1009
E.g. maybe it really never matters how "n" is code-completed in the below
Code: Pascal  [Select][+][-]
  1. type TFoo array [256] of boolean;
  2. var f: TFoo;
  3. begin
  4.   f[n {complete n via code tool} ] := true;  

This looks like a rather ordinary type inference based on a numeric literal, which may be governed by specific, predefined rules, just as in the case of type inference for constants declared without specifying the data type of the value provided.

Quote
Maybe its fine that this will just give integer. Maybe someone suddenly wants that to be byte (but "byte(256)" isn't a thing).

Tools aren't fortune-tellers; what they can deduce depends on the specific rules you apply. The compiler also determines types based solely on literals (character vs string, shortint vs longerint), which can cause problems not with the IDE, but with the program's correctness. Here's a thread discussing this topic: https://forum.lazarus.freepascal.org/index.php?topic=50921.0

Integer as the default type sounds reasonable.
« Last Edit: May 27, 2026, 09:55:41 pm by flowCRANE »
Lazarus 4.8 with FPC 3.2.2, Windows 11 — all 64-bit

Working solo on a top-down retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL3.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12760
  • Debugger - SynEdit - and more
    • wiki
E.g. maybe it really never matters how "n" is code-completed in the below
Code: Pascal  [Select][+][-]
  1. type TFoo array [256] of boolean;
  2. var f: TFoo;
  3. begin
  4.   f[n {complete n via code tool} ] := true;  

This looks like a rather ordinary type inference based on a numeric literal, which may be governed by specific, predefined rules, just as in the case of type inference for constants declared without specifying the data type of the value provided.

Quote
Maybe its fine that this will just give integer. Maybe someone suddenly wants that to be byte (but "byte(256)" isn't a thing).

Tools aren't fortune-tellers; what they can deduce depends on the specific rules you apply. The compiler also determines types based solely on literals (character vs string, shortint vs longerint), which can cause problems not with the IDE, but with the program's correctness. Here's a thread discussing this topic: https://forum.lazarus.freepascal.org/index.php?topic=50921.0

Integer as the default type sounds reasonable.

Ok, amazing that no one sees the point I am making.

Yes, integer is fine. And yes, you can't predict all...

But, in case of
   array [ TSubRange(8) ]
the compiler may maybe better give an error if no behaviour is defined? Because if TSubRange is not 0 based, then this may not intuitively mean 8 elements?

And in the example

Code: Pascal  [Select][+][-]
  1. type TFoo array [256] of boolean;
  2. var f: TFoo;
  3. begin
  4.   f[n {complete n via code tool} ] := true;  
[/quote]

In the existing syntax I can write
Code: Pascal  [Select][+][-]
  1. type TFoo = array [byte(0)..byte(255)] of boolean;

And codetools (currently failing, but could be fixed) could then complete "n" as byte.

In the new code I can't do that, if I need all 256 elements covered by a byte (unless you want to allow "byte(256)"?)

Again, the point isn't that this would need to work. The point is, that if it does not work, then it should "not work by deliberate design" rather than "not work by accident".

Anyway, I made my point. I am not affected, if the code is done on a "don't care" base (despite disagreements up till here I did not think it was). But if it is, take your pick and be happy. My original post was meant sincerely to help. If it didn't or isn't welcome, so be it.

440bx

  • Hero Member
  • *****
  • Posts: 6619
Actually, I would like to see an array form made with only the array element count because that would ease porting C code to Pascal.  Something along the lines of:

Code: Pascal  [Select][+][-]
  1. var
  2.   SomeArray : array[12] of <sometype>;
  3.  
This allows a 1:1 translation of most C arrays.  That eases porting which is a nice convenience.

Syntactically, I think it should be fairly simple to implement because the parser would be forced to interpret the decimal as a count instead of a lower bound as soon as it sees the decimal (or identifier) isn't followed by ".."

The one thing I want to reiterate is that, this is just for convenience in the porting of C code to Pascal.  It makes the initial port likely be more 1:1 than otherwise but, generally speaking, the lack of bounds is an undesirable feature, typical C stuff, sure you want 10 elements ?... no problem, here are 10 elements and you're on your own pal and, they have the gall of calling that a compiler ?  the question is: how much red paint or glue are these people sniffing ?

That construction should be seen as the first step in a road to make the original C code  what it _should_ have been but couldn't be because of all of C's syntactic deficiencies.  In the great majority of cases, it is just a band-aid to ease the porting from C to Pascal.



FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

flowCRANE

  • Hero Member
  • *****
  • Posts: 1009
Ok, amazing that no one sees the point I am making.

I see the point you are making and i think that in case of giving TSubRange( 8 ) this should produce error, because the programmer intention cannot be determined. So if introducing number-of-elements syntax, there should be clear rules about what is valid and what is not. The only thing I care and propose is the ability to specify the number of elements in the form of an positive integer (or non-negative), as a numeric literal or a constant (i.e. the name of the constant that holds an integer). IMO other things, beside those currently supported (type name like Byte, Char, TMyEnum etc.), should produce compilation error.

Quote
And codetools (currently failing, but could be fixed) could then complete "n" as byte.

Currently in such case, codetools can deduce the iterator type and produce an Integer variable:

Code: Pascal  [Select][+][-]
  1. var
  2.   Foo: array [0 .. 255] of Char;
  3. begin
  4.   for Iter := Low(Foo) to High(Foo) do
  5.     ;
  6. end.

If I place the caret on the Iter variable and press Shift+Ctrl+C, the new variable is created:

Code: Pascal  [Select][+][-]
  1. var
  2.   Foo: array [0 .. 255] of Char;
  3.   Iter: Integer;
  4. begin
  5.   for Iter := Low(Foo) to High(Foo) do
  6.     ;
  7. end.

Reasonable choice od data type, I have no objections. If the array is declared with the number of elements specified, the Integer type should also be used. What is interesting, codetools works fine even in this case:

Code: Pascal  [Select][+][-]
  1. var
  2.   Foo: array [0 .. 255] of Char;
  3. begin
  4.   for Iter in Foo do
  5.     ;
  6. end.
The Iter data type will be Char, which is also correct. Good job. 8)
« Last Edit: May 27, 2026, 11:57:11 pm by flowCRANE »
Lazarus 4.8 with FPC 3.2.2, Windows 11 — all 64-bit

Working solo on a top-down retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL3.

Fibonacci

  • Hero Member
  • *****
  • Posts: 1103
  • Behold, I bring salvation - Unleashed Pascal
    • fibo.gg
New feature: array[N] size shorthand

On by default in {$mode unleashed}. No modeswitch.

Code: Pascal  [Select][+][-]
  1. var
  2.   a: array[100] of integer;   // shorthand - new
  3.   b: array[0..99] of integer; // existing - equivalent

Shorthand for the common case: "I want N elements, starting at 0." Equivalent to array[0..N-1] of T - same memory layout, same indexing, same compile output.

Rules
  • N must be a positive integer constant expression (literal or const)
  • Multi-dimensional works: array[3, 4] of T = array[0..2, 0..3] of T
What stays unchanged

Everything else parses the way it did. The shorthand only kicks in when the bracket contains a positive integer constant.

Code: Pascal  [Select][+][-]
  1. var
  2.   a: array[Byte] of integer;       // type as index, existing
  3.   b: array[1..10] of integer;      // explicit range (non-zero start), existing
  4.   c: array[TMyEnum] of integer;    // enum as index, existing
  5.   d: array[Low..High] of integer;  // const-expression range, existing

Strictly additive, no breaking changes.
Unleashed Pascal: async/await, parallel for, match, tuples, string interpolation, inline vars, autofree, no-RTTI & tons more. Star on GitHub ⭐

flowCRANE

  • Hero Member
  • *****
  • Posts: 1009
Great job. 8)



Lately, I’ve been quite busy working on my game, specifically on world rendering. In many places, I need to swap the values of two variables, whether to swap vertices or sort elements to determine the final Z-order. Unfortunately, Free Pascal has neither an operator for swapping variable values nor even intrinsics for this purpose. There are Swap functions, but they swap bytes/words within a given variable, not between two variables.

What do you think about adding a new operator for this purpose? Inspired by Pascal syntax and similar operators from other programming languages (mostly Icon, such an operator could be :=:, and could be used in the following way:

Code: Pascal  [Select][+][-]
  1. First :=: Second; // Swap values between "First" and "Second" variable.

This would be equivalent to this:

Code: Pascal  [Select][+][-]
  1. var
  2.   Temp: Integer;
  3.  
  4. {..}
  5.  
  6. Temp   := First;
  7. First  := Second;
  8. Second := Temp

The reason is that currently neither the language nor the RTL supports a simple way to swap the values of two variables, which forces developers to allocate an additional variable, correctly write the code to swap the data between pairs of variables, and in some cases even wrap the swap in an additional grouping block (if it is the only operation, e.g., after a condition is met). Introducing a new operator would reduce the number of lines of code to one from even seven and minimize the risk of errors.

This operator would have to be used only in conjunction with two variables (just like the assignment operator) of the same data types, or similar types when dealing with some specific data types (e.g., swapping the values of variables of type Byte and Word should be valid). In addition to ensuring data type compatibility, support for range testing must also be provided. It is important here to support not only the swapping of simple data types, but also, for example, the swapping of entire structures, arrays, etc., as well as support for generics.



Let me know what you think. Personally, I believe that such an operator would be very useful, would have a wide range of applications, and would also reduce the amount of code that needs to be written, thereby minimizing the chance of errors (or even eliminating the possibility of making a mistake).

PS: Please don't use arguments like "no, because this operator is too similar to the assignment operator and it's easy to make a mistake here", because that's not a valid argument. I might just as well say that the := operator is too similar to the = operator, that >= is too similar to <=, or that += is too similar to *= (or shl is too similar to shr).
« Last Edit: May 28, 2026, 06:32:17 pm by flowCRANE »
Lazarus 4.8 with FPC 3.2.2, Windows 11 — all 64-bit

Working solo on a top-down retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL3.

Fibonacci

  • Hero Member
  • *****
  • Posts: 1103
  • Behold, I bring salvation - Unleashed Pascal
    • fibo.gg
What do you think about adding a new operator for this purpose? Inspired by Pascal syntax and similar operators from other programming languages, such an operator could be :=:, and could be used in the following way:

Code: Pascal  [Select][+][-]
  1. {$mode unleashed}
  2.  
  3. begin
  4.   var a := 'foo';
  5.   var b := 'bar';
  6.   writeln(a, ' ', b);
  7.  
  8.   (a, b) := (b, a);
  9.   writeln(a, ' ', b);
  10.  
  11.   readln;
  12. end.
Unleashed Pascal: async/await, parallel for, match, tuples, string interpolation, inline vars, autofree, no-RTTI & tons more. Star on GitHub ⭐

Thausand

  • Hero Member
  • *****
  • Posts: 618
The reason is that currently neither the language nor the RTL supports a simple way to swap the values of two variables, which forces developers to allocate an additional variable, correctly write the code to swap the data between pairs of variables, and in some cases even wrap the swap in an additional grouping block (if it is the only operation, e.g., after a condition is met). Introducing a new operator would reduce the number of lines of code to one from even seven and minimize the risk of errors.
Operator sugar idea is ok but above write not true. Can use xor trick and use 2 variables.
A docile goblin always follow HERMES.md

flowCRANE

  • Hero Member
  • *****
  • Posts: 1009
So you were inspired by a different syntax for the swap, one that’s more common than the operator. That’s fine, though personally I think the operator would be a much better choice, due to the brevity of the swap notation and its similarity to a standard assignment. The syntax you provided isn’t bad, though instead of two variables you have to specify four, plus parentheses, which unnecessarily lengthens the entire expression.

For variables with single-letter names, this doesn’t look bad, but in the case of swapping data between fields in structures, the syntax instead of this (taken from my game sources):

Code: Pascal  [Select][+][-]
  1. Quad1^.Vertex1.Y :=: Quad2^.Vertex2.Y; // Swaps two "Single" values.

we have to type this monstrosity:

Code: Pascal  [Select][+][-]
  1. (Quad1^.Vertex1.Y, Quad2^.Vertex2.Y) := (Quad2^.Vertex2.Y, Quad1^.Vertex1.Y);

which in my opinion is much less readable and too long.



Operator sugar idea is ok but above write not true. Can use xor trick and use 2 variables.

That's cool, but you can't use it to swap records or arrays, for example (without loops and taking into account variable sizes). Plus, the languege would prevent you from swapping two variables of type Single, for instance, because you can't do that without casting or overloading the xor operator. In any case, that still doesn't change the fact that even if you use the xor-trick, you still have to write three lines of code instead of one. So, given all of the above, it’s better to use an additional variable rather than the xor-trick.

The operator I’m proposing would allow swapping the data of two variables of any type and size in a single, short, and unambiguous expression. So this is why I consider proposed operator as the best way to do the swap.
« Last Edit: May 28, 2026, 06:57:26 pm by flowCRANE »
Lazarus 4.8 with FPC 3.2.2, Windows 11 — all 64-bit

Working solo on a top-down retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL3.

Thausand

  • Hero Member
  • *****
  • Posts: 618
That's cool, but you can't use it to swap records or arrays, for example (without loops and taking into account variable sizes). Plus, the languege would prevent you from swapping two variables of type Single, for instance, because you can't do that without casting or overloading the xor operator. In any case, that still doesn't change the fact that even if you use the xor-trick, you still have to write three lines of code instead of one. So, given all of the above, it’s better to use an additional variable rather than the xor-trick.
Yes, that true. I forgot and add write "for number integer".

Quote
The operator I’m proposing would allow swapping the data of two variables of any type and size in a single, short, and unambiguous expression. So this is why I consider proposed operator as the best way to do the swap.
Yes, and when make operator then can make self with overload (when have support for overload this new operator). Base idea is good :)
A docile goblin always follow HERMES.md

flowCRANE

  • Hero Member
  • *****
  • Posts: 1009
It should be possible to overload such an operator, but under the same rules that apply to overloading existing operators. For example, overloading the swap operator for two integers should be prohibited, just as it is currently prohibited to overload, for example, the operator for adding two integers (this results in a "impossible operator overload" compilation error).

But the problem is that such an operator couldn't be used inside the overloaded operator's function (for the same type), because that would cause an infinite recursive call to the operator's function.

Code: Pascal  [Select][+][-]
  1. operator :=: (var LHS: Integer; var RHS: Single);
  2. begin
  3.   LHS :=: RHS; // recursive call
  4. end;

Perhaps overloading this operator should be prohibited to avoid such odd cases. Besides, inside the overloaded function, you’d have to perform a standard swap with an additional variable anyway, so the overloaded version wouldn’t likely see much use.
« Last Edit: May 28, 2026, 07:09:38 pm by flowCRANE »
Lazarus 4.8 with FPC 3.2.2, Windows 11 — all 64-bit

Working solo on a top-down retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL3.

Fibonacci

  • Hero Member
  • *****
  • Posts: 1103
  • Behold, I bring salvation - Unleashed Pascal
    • fibo.gg
With :=: I can smell problems, syntax errors, edge cases. I'm thinking of adding an intrinsic that would swap 2 variables, like Swap(a, b). Before I do, you folks can discuss the form, but as I said - :=: spells trouble, I can smell it.
Unleashed Pascal: async/await, parallel for, match, tuples, string interpolation, inline vars, autofree, no-RTTI & tons more. Star on GitHub ⭐

flowCRANE

  • Hero Member
  • *****
  • Posts: 1009
With :=: I can smell problems, syntax errors, edge cases.

Now you have to check more edge cases and do more parsing, because instead of two identifiers there are four; instead of simply providing the identifiers, you have to enclose them in parentheses, and on top of that, you have to check their order in each pair to make sure that the entire construct will actually trigger a swap. Much more to parse for no real reason.

In the case of the operator, there are only two variables—and thus two identifiers—and their order doesn’t matter (A :=: B is equivalent to B :=: A); furthermore, there are no parentheses. There’s less to check, less work for the compiler, less code to write, and fewer opportunities to make errors/bugs.

Quote
I'm thinking of adding an intrinsic that would swap 2 variables, like Swap(a, b).

Impossible with this name, as the Swap functions already exists.
« Last Edit: May 28, 2026, 08:00:59 pm by flowCRANE »
Lazarus 4.8 with FPC 3.2.2, Windows 11 — all 64-bit

Working solo on a top-down retro-style action/adventure game (pixel art), programming the engine from scratch, using Free Pascal and SDL3.

440bx

  • Hero Member
  • *****
  • Posts: 6619
:=: spells trouble, I can smell it.
And you're right.  Currently the compiler, when sitting on a ":" can determine the difference between a single ":" and a ":=" by looking at the next token but, if ":=:" was also allowed then the compiler would have to look two tokens ahead to determine it is dealing with a ":=:" instead of just an assignment ":=".  That would add unnecessary complexity to the parser and would definitely impact performance enough that it might actually be noticeable in most cases because ":" and ":=" are, very, very common.

In addition to that, if there is a syntax error involving a ":" emitting a reasonably decent message as to what the cause is, is difficult because there are too many possible cases.

Definitely don't use ":=:".
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

creaothceann

  • Sr. Member
  • ****
  • Posts: 471
N must be a positive integer constant expression (literal or const)

Positive and non-zero integer, right?


operator

IMO relying too much on operators in source code tends to make it look like line noise. I'd rather have a built-in subroutine.

 

TinyPortal © 2005-2018