Proper preprocessor?

classic Classic list List threaded Threaded
77 messages Options
1234
Reply | Threaded
Open this post in threaded view
|

Re: Proper preprocessor?

Ryan Joseph


> On Jun 22, 2018, at 3:24 PM, denisgolovan <[hidden email]> wrote:
>
> On the other hand, recent language Rust has macros nicely integrated in language itself and they plan to extend them in 2.0 version.
> D language also has mixins. Let alone Lisp-dynamics derivatives.

Please post some examples if you have them, I’m curious. :)

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Proper preprocessor?

Free Pascal - General mailing list
In reply to this post by denisgolovan
denisgolovan <[hidden email]> schrieb am Fr., 22. Juni 2018, 10:24:
Generics are rather limited in that respect.
At least some construction should exist to instantiate those generics.
e.g. to create several public structs, interfaces, free functions (possibly instancing generics) in one go.

Do you have examples here? 

Regards, 
Sven 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Proper preprocessor?

Marco van de Voort
In reply to this post by Ryan Joseph
In our previous episode, Ryan Joseph said:
>
> Here?s an example of something I?ve seen for debugging. I think that was kind of cool you could print types like that and I?m not sure how that would work in Pascal if at all. Maybe some RTTI magic perhaps.
>
> {$define typestr(t):='#t: '+IntToStr(sizeof(#t))}

(highlights not having an open array of byte like syntax that also conveys
size)

 
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Proper preprocessor?

Free Pascal - General mailing list
In reply to this post by Michael Van Canneyt
Michael Van Canneyt <[hidden email]> schrieb am Fr., 22. Juni 2018, 08:01:


On Fri, 22 Jun 2018, Ryan Joseph wrote:

>
>
>> On Jun 22, 2018, at 12:21 PM, Michael Van Canneyt <[hidden email]> wrote:
>>
>> 'Nice' is not an argument.
>>
>> If someone else assumes that assert() works as expected - i.e. throws an exception, then your macro will mess up his code, leading to
>> unpredictable results.
>>
>> From my - admittedly subjective - point of view, your examples only serve to demonstrate why we should definitely not support macros...
>
> For any example I can give it’s easy to extrapolate into some scenario where it would be a disaster. I get that. The reason I ever wanted to make a macro was for personal use to give some project specific meaning to some construct or a quick hack. Frameworks like Apple uses,  often use macros to document parts of code in a way which otherwise isn’t part of the language and no suitable for comments. I’m not saying this is how we all should program. I don’t suggest people go out and start using macros in place of functions.
>
> Btw why was the $define:= syntax ever introduced in the first place?
> adding a parameter to the syntax is a minor extension but it sounds like
> Pascal programers here really don’t like it in general.

A good and just question. We most likely didn't realize the consequences.
Meanwhile we're older, more experienced and we now know what impact seemingly
good ideas can have...

They were added for MacPascal compatibility: http://wiki.freepascal.org/Mode_MacPas

Regards, 
Sven 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Proper preprocessor?

Michael Van Canneyt


On Fri, 22 Jun 2018, Sven Barth via fpc-pascal wrote:

>> A good and just question. We most likely didn't realize the consequences.
>> Meanwhile we're older, more experienced and we now know what impact
>> seemingly
>> good ideas can have...
>>
>
> They were added for MacPascal compatibility:
> http://wiki.freepascal.org/Mode_MacPas

Eh, no. I think macros predate macpas mode.

But exported macros were indeed introduced for mode macpas.

Michael.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Proper preprocessor?

denisgolovan
In reply to this post by Free Pascal - General mailing list
> Do you have examples here?

There are several use-cases I often see.

1. The task is to allow declaration of some container structure (vector, tree, etc.)
The mechanism to create it must have enough flexibility to define/parametrize at compile-time in one go:
- keys are integers in 0..N-1 only or any type supporting equality testing.
  That user decision should influence container code and its manual code optimizations, e.g. special cases like indexing, appending/removing/etc.
- are keys always sorted? => influences insertion, search, etc.
- values are primitive types (not having destruction phase - just dealloc) or it should call Finalize/Free/"Assign nil" on destroying.
- if values are records - all accessors should return pointers instead to be able to modify fields without "read, modify, write" ceremonies.
- does structure generate events on insertion/deleting/etc? => influences event property declarations + actual code to trigger those
- what kind of interface user prefers for the container - class, record, interface with reference counting or any combination of those?
- all its generic functions like map, filter, destructive map, etc. should be overloaded functions existing both as structure methods and free form (globals with additional argument). Their argument should accept all kinds of callbacks - simple functions, methods ("of object" type), nested ("is nested") and possibly future "reference" (closure FPC does not support yet).
- grow strategy - user-defined, with given coefficient, etc.

... currently all above is generated using m4 producing entire FPC module.
That's old times philosophy - structures amount is small, functions to work with them are numerous and coherently named. Something similar to C++ STL, but without exponential build times and more tailored to users' needs.

2. Meta declarations for global entities with names/ids/descriptions/etc.
The mechanism to create it must have enough flexibility to define/parametrize at compile-time in one go:
- create Pascal native declarations as enums
- create Pascal functions to convert to/from strings/integers/etc
- create overloaded serialization functions for XML, streams, etc.
- create foreign code declaration (C, Lua, etc.)
- native Pascal foreign code marshalling (e.g. Pascal <-> Lua via Lua stack)

3. Dynamic libraries exports.
The mechanism to create it must have enough flexibility to define/parametrize at compile-time in one go:
- compile in Pascal "generics" / m4 templates to support other processes / languages
- exported functions should accessible via specific foreign import (e.g. it should generate corresponding header files for use in C/C++)

4. Published functions/classes/ into interpreted languages
- just declare which classes and their methods should be accessible e.g. from Lua and code should generated for make it happen.
- FFI import/export - declare functions / types / names - get boilerplate to make it happen.


That's more or less it.
At least those I quickly gathered from real project.

--
Regards,
Denis Golovan
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Proper preprocessor?

Michael Van Canneyt


On Fri, 22 Jun 2018, denisgolovan wrote:

>> Do you have examples here?
>
> There are several use-cases I often see.
>
> 1. The task is to allow declaration of some container structure (vector, tree, etc.)
> 2. Meta declarations for global entities with names/ids/descriptions/etc.
> 3. Dynamic libraries exports.
> 4. Published functions/classes/ into interpreted languages
>
> That's more or less it.
> At least those I quickly gathered from real project.

All these use cases I also have. But I use code generators for this.
The source does not need to be pascal (although it can be).
It just needs some structured format, from which Pascal code is then generated.

Currently I employ code generators (written in pascal) that start from XML, JSON,
IDL, or datasets (and in some limited cases pascal code).
They generate the target code, plus unit test testsuites.

using fcl-passrc it is really easy to generate bindings for e.g. lua or any
other embedded pascal scripting tool.

Michael.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Proper preprocessor?

denisgolovan
In reply to this post by Ryan Joseph
See below the function to convert dynamic value into string value inside interpreter project I am working at, something similar to *ToStr family in FPC.

It's generated by the macro which expands to corresponding branches for each value type. "$" prefixes are for passing macro arguments. "ident!" is "calling" other macros, but it's quite obvious I guess.

Last syntax expression is "application" of this macro resulting in actual function creating and immediate compilation by Rust compiler.

macro_rules! to_string {
    ($($scalar:tt),*) => {
        pub fn to_string(ast: &AST, interpreter: &Interpreter) -> AST {
            match base_tp(ast.tp()) {
                $(
                    $scalar => {
                        let aco=aco!($scalar, ast, Some(interpreter.alloc()));
                        let s=ato_str!(aco, aget!(aco, ast), interpreter);
                        new_string(&s, interpreter.alloc())
                    },
                )*
                $(
                    to_vec!($scalar) => {
                        let aco=aco!($scalar, ast, Some(interpreter.alloc()));
                        let v=ast.array::<atype!($scalar)>();
                        v.iter().map(|x| {
                            let s=ato_str!(aco, *x, interpreter);
                            new_string(&s, interpreter.alloc())
                        }).enlist(v.len(), interpreter.alloc())
                    }
                )*
                $(
                    to_deq!($scalar) => {
                        let aco=aco!($scalar, ast, Some(interpreter.alloc()));
                        let v=ast.vecdeq().head::<atype!($scalar)>();
                        v.iter().map(|x| {
                            let s=ato_str!(aco, *x, interpreter);
                            new_string(&s, interpreter.alloc())
                        }).enlist(v.len(), interpreter.alloc())
                    }
                )*
                x if is_nested(x) => atomic(ast, to_string, "to_string", interpreter),
                VEC_CHAR => (*ast).clone(),

                _ => except!(eval_err!("cast: nyi.")),
            }
        }
    }
}
to_string!(
    SC_BOOL,
    SC_BYTE,
    SC_SHORT,
    SC_INT,
    SC_MONTH,
    SC_DATE,
    SC_MINUTE,
    SC_SECOND,
    SC_TIME,
    SC_LONG,
    SC_TIMESTAMP,
    SC_DATETIME,
    SC_TIMESPAN,
    SC_SINGLE,
    SC_DOUBLE,
    SC_ENUM,
    SC_SYMBOL,
    SC_GUID
);

All in all my rough estimate for macro efficiency ratio is something like 12x currently. That's about 12 times less code to type and support. YMMV, of course.

See https://danielkeep.github.io/tlborm/book/mbe-min-captures-and-expansion-redux.html for boring details on Rust macros.

BR,
Denis
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Proper preprocessor?

Free Pascal - General mailing list
In reply to this post by Ryan Joseph
Am 22.06.2018 um 10:12 schrieb Ryan Joseph:

>
>> On Jun 22, 2018, at 12:24 PM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>>
>> If $Assertions is set to Off the complete Assert() line will be absent from the compiled code.
>
> Good to know thanks.
>
>
> Here’s an example of something I’ve seen for debugging. I think that was kind of cool you could print types like that and I’m not sure how that would work in Pascal if at all. Maybe some RTTI magic perhaps.
>
> {$define typestr(t):='#t: '+IntToStr(sizeof(#t))}
>
> program macro_test;
> uses
> SysUtils;
>
> type
> MyRecord = record
> x, y, z: single;
> end;
>
> begin
> writeln(typestr(MyRecord)); // MyRecord: 12
> end.
In trunk that can be done rather nicely:

=== code begin ===

program ttest;

{$mode objfpc}

uses
   TypInfo;

type
   TMyRecord = record
     x, y, z: Single;
   end;

generic function TypeStr<T>: String;
var
   ti: PTypeInfo;
begin
   ti := PTypeInfo(TypeInfo(T));
   WriteStr(Result, ti^.Name, ': ', SizeOf(T));
end;

begin
   Writeln(specialize TypeStr<TMyRecord>);
end.

=== code end ===

Regards,
Sven
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Proper preprocessor?

Florian Klämpfl
Am 22.06.2018 um 22:07 schrieb Sven Barth via fpc-pascal:

> Am 22.06.2018 um 10:12 schrieb Ryan Joseph:
>>
>>> On Jun 22, 2018, at 12:24 PM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>>>
>>> If $Assertions is set to Off the complete Assert() line will be absent from the compiled code.
>>
>> Good to know thanks.
>>
>>
>> Here’s an example of something I’ve seen for debugging. I think that was kind of cool you could print types like that
>> and I’m not sure how that would work in Pascal if at all. Maybe some RTTI magic perhaps.
>>
>> {$define typestr(t):='#t: '+IntToStr(sizeof(#t))}
>>
>> program macro_test;
>> uses
>>     SysUtils;
>>
>> type
>>     MyRecord = record
>>         x, y, z: single;
>>     end;
>>
>> begin
>>     writeln(typestr(MyRecord)); // MyRecord: 12
>> end.
> In trunk that can be done rather nicely:
>
> === code begin ===
>
> program ttest;
>
> {$mode objfpc}
>
> uses
>    TypInfo;
>
> type
>    TMyRecord = record
>      x, y, z: Single;
>    end;
>
> generic function TypeStr<T>: String;
> var
>    ti: PTypeInfo;
> begin
>    ti := PTypeInfo(TypeInfo(T));
>    WriteStr(Result, ti^.Name, ': ', SizeOf(T));
> end;
>
> begin
>    Writeln(specialize TypeStr<TMyRecord>);
> end.
>
> === code end ===

Or even

program ttest;

{$mode objfpc}

uses
   TypInfo;

type
   TMyRecord = record
     x, y, z: Single;
   end;

generic function _TypeStr<T>: String;
var
   ti: PTypeInfo;
begin
   ti := PTypeInfo(TypeInfo(T));
   WriteStr(Result, ti^.Name, ': ', SizeOf(T));
end;

{$macro on}

{$define TypeStr:=specialize _TypeStr}

begin
   Writeln(TypeStr<TMyRecord>);
end.


;)

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Proper preprocessor?

Free Pascal - General mailing list
Am 22.06.2018 um 22:13 schrieb Florian Klämpfl:

> Am 22.06.2018 um 22:07 schrieb Sven Barth via fpc-pascal:
>> Am 22.06.2018 um 10:12 schrieb Ryan Joseph:
>>>
>>>> On Jun 22, 2018, at 12:24 PM, Sven Barth via fpc-pascal
>>>> <[hidden email]> wrote:
>>>>
>>>> If $Assertions is set to Off the complete Assert() line will be
>>>> absent from the compiled code.
>>>
>>> Good to know thanks.
>>>
>>>
>>> Here’s an example of something I’ve seen for debugging. I think that
>>> was kind of cool you could print types like that and I’m not sure
>>> how that would work in Pascal if at all. Maybe some RTTI magic perhaps.
>>>
>>> {$define typestr(t):='#t: '+IntToStr(sizeof(#t))}
>>>
>>> program macro_test;
>>> uses
>>>     SysUtils;
>>>
>>> type
>>>     MyRecord = record
>>>         x, y, z: single;
>>>     end;
>>>
>>> begin
>>>     writeln(typestr(MyRecord)); // MyRecord: 12
>>> end.
>> In trunk that can be done rather nicely:
>>
>> === code begin ===
>>
>> program ttest;
>>
>> {$mode objfpc}
>>
>> uses
>>    TypInfo;
>>
>> type
>>    TMyRecord = record
>>      x, y, z: Single;
>>    end;
>>
>> generic function TypeStr<T>: String;
>> var
>>    ti: PTypeInfo;
>> begin
>>    ti := PTypeInfo(TypeInfo(T));
>>    WriteStr(Result, ti^.Name, ': ', SizeOf(T));
>> end;
>>
>> begin
>>    Writeln(specialize TypeStr<TMyRecord>);
>> end.
>>
>> === code end ===
>
> Or even
>
> program ttest;
>
> {$mode objfpc}
>
> uses
>   TypInfo;
>
> type
>   TMyRecord = record
>     x, y, z: Single;
>   end;
>
> generic function _TypeStr<T>: String;
> var
>   ti: PTypeInfo;
> begin
>   ti := PTypeInfo(TypeInfo(T));
>   WriteStr(Result, ti^.Name, ': ', SizeOf(T));
> end;
>
> {$macro on}
>
> {$define TypeStr:=specialize _TypeStr}
>
> begin
>   Writeln(TypeStr<TMyRecord>);
> end.
>
>
> ;)
Those people can also use mode Delphi if they want :P

Regards,
Sven
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Proper preprocessor?

Florian Klämpfl
In reply to this post by Michael Van Canneyt
Am 22.06.2018 um 08:01 schrieb Michael Van Canneyt:

>
>
> On Fri, 22 Jun 2018, Ryan Joseph wrote:
>
>>
>>
>>> On Jun 22, 2018, at 12:21 PM, Michael Van Canneyt <[hidden email]> wrote:
>>>
>>> 'Nice' is not an argument.
>>>
>>> If someone else assumes that assert() works as expected - i.e. throws an exception, then your macro will mess up his
>>> code, leading to
>>> unpredictable results.
>>>
>>> From my - admittedly subjective - point of view, your examples only serve to demonstrate why we should definitely not
>>> support macros...
>>
>> For any example I can give it’s easy to extrapolate into some scenario where it would be a disaster. I get that. The
>> reason I ever wanted to make a macro was for personal use to give some project specific meaning to some construct or a
>> quick hack. Frameworks like Apple uses,  often use macros to document parts of code in a way which otherwise isn’t
>> part of the language and no suitable for comments. I’m not saying this is how we all should program. I don’t suggest
>> people go out and start using macros in place of functions.
>>
>> Btw why was the $define:= syntax ever introduced in the first place? adding a parameter to the syntax is a minor
>> extension but it sounds like
>> Pascal programers here really don’t like it in general.
>
> A good and just question. We most likely didn't realize the consequences. Meanwhile we're older, more experienced and we
> now know what impact seemingly good ideas can have...

Well, I think we (the people who decided to implement it) thought about the consequences. The chances to mess around
with the currently implemented macro support is far less than with macros with parameters and concat operations. And the
impact is limited imo.

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Proper preprocessor?

Marcos Douglas B. Santos
In reply to this post by Florian Klämpfl
On Fri, Jun 22, 2018 at 5:13 PM, Florian Klämpfl <[hidden email]> wrote:
>
> {$macro on}
>
> {$define TypeStr:=specialize _TypeStr}
>
> begin
>   Writeln(TypeStr<TMyRecord>);
> end.

Can I use the same idea but for units?

Like this:

{$macro on}

uses
  {$ifdef debug}
    foo_debug
    {$define foo := foo_debug}
  {else}
    foo_run;
    {$define foo := foo_run}
 {$enfif}

begin
  foo.SomeFunc;
end;
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Proper preprocessor?

Free Pascal - General mailing list
Am 22.06.2018 um 23:10 schrieb Marcos Douglas B. Santos:

> On Fri, Jun 22, 2018 at 5:13 PM, Florian Klämpfl <[hidden email]> wrote:
>> {$macro on}
>>
>> {$define TypeStr:=specialize _TypeStr}
>>
>> begin
>>    Writeln(TypeStr<TMyRecord>);
>> end.
> Can I use the same idea but for units?
>
> Like this:
>
> {$macro on}
>
> uses
>    {$ifdef debug}
>      foo_debug
>      {$define foo := foo_debug}
>    {else}
>      foo_run;
>      {$define foo := foo_run}
>   {$enfif}
>
> begin
>    foo.SomeFunc;
> end;

Why not simply test it?

That said you can also do this:

=== code begin ===

{$macro on}

{$ifdef debug}
   {$define foo := foo_debug}
{$else}
   {$define foo := foo_run}
{$endif}

uses
   foo;

begin
   foo.SomeFunc;
end.

=== code end ===

Though more often than not such things are done by simply using a
different set of units with the same names in a different unit path.

Regards,
Sven
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Proper preprocessor?

Ryan Joseph
In reply to this post by Florian Klämpfl


> On Jun 23, 2018, at 3:13 AM, Florian Klämpfl <[hidden email]> wrote:
>
> {$macro on}
>
> {$define TypeStr:=specialize _TypeStr}
>
> begin
>  Writeln(TypeStr<TMyRecord>);
> end.
>
>
> ;)

You have a good sense of humor about it at least. :)

So you can in fact print types, albeit with a more verbose syntax and by using another meta-programming tactic, e.g. generics.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Proper preprocessor?

Florian Klämpfl
Am 23.06.2018 um 04:30 schrieb Ryan Joseph:

>
>
>> On Jun 23, 2018, at 3:13 AM, Florian Klämpfl <[hidden email]> wrote:
>>
>> {$macro on}
>>
>> {$define TypeStr:=specialize _TypeStr}
>>
>> begin
>>   Writeln(TypeStr<TMyRecord>);
>> end.
>>
>>
>> ;)
>
> You have a good sense of humor about it at least. :)
>
> So you can in fact print types, albeit with a more verbose syntax and by using another meta-programming tactic, e.g. generics.
>

Nobody said meta-programming is bad. But the right approach should be used.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Proper preprocessor?

Marcos Douglas B. Santos
In reply to this post by Free Pascal - General mailing list
On Fri, Jun 22, 2018 at 6:47 PM, Sven Barth via fpc-pascal
<[hidden email]> wrote:

>
>> [...]
>
> Why not simply test it?
>
> That said you can also do this:
>
> === code begin ===
>
> {$macro on}
>
> {$ifdef debug}
>   {$define foo := foo_debug}
> {$else}
>   {$define foo := foo_run}
> {$endif}
>
> uses
>   foo;
>
> begin
>   foo.SomeFunc;
> end.
>
> === code end ===
>
> Though more often than not such things are done by simply using a different
> set of units with the same names in a different unit path.

Because I was on the road, without my laptop, and I didn't want to
forget this :)
And your way is even better, thank you.

Best regards,
Marcos Douglas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
1234