Pliant forum message

Concerning release: 33
Subject: Contribution/Question
Title: Critique of my first meta program (evar.pli)
initial message posted by John Eikenberry on 2000/03/13 12:25:59
After much toiling away I finally realized I was making things more difficult 
than necessary. So I went for the simple/direct approach. Below then is my try at 
creating a meta function that handles wrapping an (C library) external global 
variable. It seems to work in simple tests (included), but I haven't had the 
chance to try it in a real application yet.

I'm curious if this seems the correct and appropriate way to form this meta
function, and any comments or critiques on how I did it. 

Of particular interest is the way I tried to not pollute the namespace to much 
by using all the underscores in my naming (like __func__ below). Is there a 
better way to not pollute the namespace when doing something like this.

----- evar.pli -----
module "/pliant/language/compiler.pli"

named_expression ext_var
  function FUNC_VAR_NAME
    EFUN_BODY
  gvar VAR_PNT_TYPE VAR_NAME :> (the_function FUNC_VAR_NAME):executable map VAR_TYPE
  function DA_FUNC
    VAR_NAME :> (the_function FUNC_VAR_NAME):executable map VAR_TYPE
  gvar DelayedAction DA_VAR
  DA_VAR function :> (the_function DA_FUNC)
  pliant_restore_actions append addressof:DA_VAR

meta evar e
  if e:size<>3
    return
  if e:2:0:size<>2
    return

  var Link:Expression var_decl :> expression duplicate ext_var
  var_decl substitute "EFUN_BODY" e:2:0
  var_decl substitute "VAR_NAME" e:1
  var_decl substitute "FUNC_VAR_NAME" (expression ident "__func__"+e:1:ident)
  var_decl substitute "DA_FUNC" (expression ident "__da_func__"+e:1:ident)
  var_decl substitute "DA_VAR" (expression ident "__da_var__"+e:1:ident)
  var_decl substitute "VAR_TYPE" e:0
  var_decl substitute "VAR_PNT_TYPE" ((expression immediat (Pointer:VAR_TYPE)) substitute "VAR_TYPE" e:0)
  var_decl near e
  e compile_as var_decl

# here's an example interface to a couple global variable in the slang library
evar Int sltt_screen_rows
  external slib "SLtt_Screen_Rows"

evar Int sltt_screen_cols
  external slib "SLtt_Screen_Cols"

console sltt_screen_rows eol
sltt_screen_rows := 10
console sltt_screen_rows eol

console sltt_screen_cols eol
sltt_screen_cols := 10
console sltt_screen_cols eol
answer posted by Hubert Tonneau on 2000/03/13 13:38:49
Really nice stuff indead !

> Of particular interest is the way I tried to not pollute the namespace to much 
> by using all the underscores in my naming (like __func__ below). Is there a 
> better way to not pollute the namespace when doing something like this.named_expression ext_var

Use names ending with an underscore, then call 'auto_rename' method:

 named_expression ext_var
   function FUNC_VAR_NAME_
     EFUN_BODY
   gvar VAR_PNT_TYPE VAR_NAME :> (the_function FUNC_VAR_NAME_):executable map VAR_TYPE
   function DA_FUNC_
     VAR_NAME :> (the_function FUNC_VAR_NAME_):executable map VAR_TYPE
   gvar DelayedAction DA_VAR_
   DA_VAR function :> the_function DA_FUNC_
   pliant_restore_actions append addressof:DA_VAR_

 meta evar e
   if e:size<>3
     return
   if e:2:0:size<>2
     return

   var Link:Expression var_decl :> expression duplicate ext_var
   var_decl substitute "EFUN_BODY" e:2:0
   var_decl substitute "VAR_NAME" e:1
   var_decl auto_rename
   var_decl substitute "VAR_TYPE" e:0
   var_decl substitute "VAR_PNT_TYPE" ((expression immediat Pointer:VAR_TYPE) substitute "VAR_TYPE" e:0)
   var_decl near e
   e compile_as var_decl
answer posted by John Eikenberry on 2000/03/13 14:33:36
Thanks for the vote of confidence... and for the auto_rename tip. Very handy.

As a side note to this, I think the enumerated type would make a good addition
to the meta programming samples page. I found it very helpful in working on this, 
and there isn't an example of creating a new type dynamically on it yet.

Thanks again.

One last thing. For anyone who is thinking of using the above. Hubert's rewrite
(with auto_rename) is what I'm using, but he missed one trailing underscore on 
this line:

   DA_VAR function :> the_function DA_FUNC_

Is should be:

   DA_VAR_ function :> the_function DA_FUNC_

answer posted by Hubert Tonneau on 2000/03/13 15:01:56
A few more remarks about your program:

- the prototype of a delayed action function recorded in
  'pliant_restore_actions' list must be:

  function xxx parameter filehandle
    arg Address parameter ; arg Int filehandle

  Also you don't care of both 'parameter' and 'filehandle' arguments.
  If you don't do that, your program will crash if I switch the calling
  convension from registers to stack only.

- When you access an argument, you must have check that it exists:

  if e:2:0:size<>2 # will crash is e:2 has no arguments
     return
 
  Should be:

  if e:2:size<>1 or e:2:0:size<>2
    return

- adding this would improve the error checking capabilities of you meta:

  if e:2:0:ident<>"external"
    return

- In your notation, you specify two times that the variable is external:
  through the 'e' of 'evar' and through the 'external' keyword. I'd rather
  use:

  evar Int sltt_screen_rows slib "SLtt_Screen_Rows"

  or

  var Int sltt_screen_rows
    external slib "SLtt_Screen_Rows"

  I prefer the first one because it's more straight forward
  (see implementation at end), but both could be valid.

- Another purely cosmetical comment:

  var_decl near e

  instruction is probably not needed because 'compile_as' method will do it.


module "/pliant/language/compiler.pli"

named_expression ext_var
  function external_function_
    external DLL ID
  gvar Pointer:TYPE NAME :> (the_function external_function_):executable map TYPE
  function restore_ parameter filehandle
    arg Address parameter ; arg Int filehandle
    NAME :> (the_function external_function_):executable map TYPE
  gvar DelayedAction da_
  da_ function :> the_function restore_  Address Int
  pliant_restore_actions append addressof:da_

meta evar e
  if e:size<>4 or (e:0 constant Type)=null or e:1:ident="" or not (e:2 cast Str) or not (e:3 cast Str)
    return
  var Link:Expression decl :> expression duplicate ext_var
  decl substitute "TYPE" e:0
  decl substitute "NAME" e:1
  decl substitute "DLL" e:2
  decl substitute "ID" e:3
  decl auto_rename
  e compile_as decl

export evar
answer posted by Hubert Tonneau on 2000/03/13 15:24:37
Also I have not tested that it really works, this could be a more
straight forward implementation:

module "/pliant/language/compiler.pli"

named_expression ext_var
  function external_function_
    external DLL ID
  function NAME -> v
    arg_RW TYPE v
    v :> (the_function external_function_):executable map TYPE

meta evar e
  if e:size<>4 or (e:0 constant Type)=null or e:1:ident="" or not (e:2 cast Str) or not (e:3 cast Str)
    return
  e compile_as (expression duplicate ext_var auto_rename substitute TYPE e:0 substitute NAME e:1 substitute DLL e:2 substitute ID e:3)

export evar
answer posted by John Eikenberry on 2000/03/14 02:16:02
Thanks for the comments... here's a few questions in return. ;)

I wasn't sure about the DelayedAction part and I wasn't sure how to test it.
Is there a way to explicitally test delayed actions? Related to this, in the
version in your last reply there isn't a DelayedAction. I assume you thinking 
that by defining the argument as arg_RW that the DA isn't needed? I'll play with
this tonight.

> - In your notation, you specify two times that the variable is external:
>   through the 'e' of 'evar' and through the 'external' keyword.

Hmm... I was shooting for a syntax similar to the external function syntax. 
Which is why I had the redundant 'external' in there. Also I think this makes
the external nature of the variable more explicit. So I think I'm leaning 
toward the 2nd.

I'll mess around with some variations and repost what I finally decide upon.
answer posted by Hubert Tonneau on 2000/03/14 04:14:27
> Is there a way to explicitally test delayed actions?

Put a 'console' instruction in it.

> Related to this, in the
> version in your last reply there isn't a DelayedAction.

In my last post, there is no more DelayedAction since the variable is
a function instead, so extracting the function address will be done each
time the variable is used, whereas with your implementation (the new symbol
is really a variable), it must be updated on .dump reload since the
executable address of the external function (so the address of the
external variable in facts) may have changed.

It is one import, and carefully designed part of Pliant syntax: a variable
is noted the same way as a function with no argument, so when you use
an identifyer, it can be any of them.

My program would be buggy if the constants evaluation algorithm in
/pliant/language/optimizer/consteval.pli
decides to evaluate the expression (the_function external_function_):executable
at compile time. It is not the case, because if you extend my program
with the following extra checkings, it will still compile:

module "/pliant/language/compiler.pli"

meta must_not_be_constant e
  if e:size=2
    var Pointer:Type t :> (e:0 constant Type) map Type
    if addressof:t<>null and (e:1 constant t)=null
      e set_void_result
      # if we succeed to compile this it means that the second argument
      # cannot be a constant with the type of the first argument
    
named_expression ext_var
  function external_function_
    external DLL ID
  function NAME -> v
    arg_RW TYPE v
    v :> (the_function external_function_):executable map TYPE
  must_not_be_constant Address (the_function external_function_):executable

meta evar e
  if e:size<>4 or (e:0 constant Type)=null or e:1:ident="" or not (e:2 cast Str) or not (e:3 cast Str)
    return
  e compile_as (expression duplicate ext_var auto_rename substitute TYPE e:0 substitute NAME e:1 substitute DLL e:2 substitute ID e:3)

> Also I think this make the external nature of the variable more explicit.
> So I think I'm leaning toward the 2nd.

Ok, you can do that, with the following extra constains:
- you should still implement the external variable as a function, just as
  I did , because if you implement it as a pointer, nothing will prevent
  a bad behaved program to change the pointer using the ':>' instruction.
- since you choose the more complex syntax, you have to carefully check
  all arguments, not only the ones that are meaningfull for you meta, so
  that your meta does not conflict with other ones. You need things like:
  if e:size<>3
     return
  if e:2:size<>1 or e:2:ident<>"{}"
    return
  if e:2:0:size<>2 or e:2:0:ident<>"external"
    return
  ...
answer posted by John Eikenberry on 2000/03/15 06:58:32
> named_expression ext_var
>   function external_function_
>     external DLL ID
>   function NAME -> v
>     arg_RW TYPE v
>     v :> (the_function external_function_):executable map TYPE

Using this method, how do you assign a new value to the variable? It doesn't seem
like it would be possible to me? Unless there is something I don't understand
about how the arg_RW works. But based on my testing, it doesn't work. Ie. I get 
an error when I try to assign a value to the global variable (like I did in the
example in my original post). Here's the error:

Failed to compile :=   (Int c  ?)
  compile file:evar.pli (internals) 36 18
  compile file:evar.pli (internals) 36 1
  compile file:evar.pli (internals) 36 1
  parse file:evar.pli (internals) 37 1
  module file:evar.pli
answer posted by Hubert Tonneau on 2000/03/15 15:12:45
> Failed to compile :=   (Int c  ?)

This error message says that ':=' could not compile because it's
first argument is a constant integer.

There is nothing wrong about this meta, but something is wrong
elsewhere (the prototype of the 'executable' field in the
'Function' data type is wrong, and the constants evaluation
algorithm is also wrong because it will sometime turn a read write
variable to a constant).
I will investigate this futher in order to correct it before release 34.

In the meantime, you can make your program work through inserting
a 'has_side_effects' instruction that will prevent constants evaluation.

named_expression ext_var
  function external_function_
    external DLL ID
  function NAME -> v
    arg_RW TYPE v
    has_side_effects
    v :> (the_function external_function_):executable map TYPE
answer posted by John Eikenberry on 2000/03/16 12:15:52
As promised, here is my (current) final version on evar.pli. Of course, based on
the current discussions on casting, this may change. Anyways... here it is:

--- evar.pli ---

module "/pliant/language/compiler.pli"

named_expression ext_var
  function external_function_
    EFUN_BODY
  function NAME -> v
    arg_RW TYPE v
    has_side_effects
    v :> (the_function external_function_):executable map TYPE

meta var e
  if e:size<>3 or (e:0 constant Type)=null
    return
  if e:2:size<>1 or e:2:ident<>"{}"
    return
  if e:2:0:size<>2 or e:2:0:ident<>"external"
    return    
    
  var Link:Expression var_decl :> expression duplicate ext_var
  var_decl substitute "TYPE" e:0
  var_decl substitute "NAME" e:1
  var_decl substitute "EFUN_BODY" e:2:0
  var_decl auto_rename
  e compile_as var_decl

export var

------

You'll notice this is a little different in format than Hubert's similar version.
That's because I prefer the substitutes broken up over multiple lines. I think
this makes what's going on more obvious and keeps any one line from getting too 
long (as Pliant doesn't have line wrapping).
answer posted by Patrice Ossona de Mendez on 2000/03/16 13:40:44
"I prefer the substitutes broken up over multiple lines. I think
 this makes what's going on more obvious and keeps any one line from getting too 
 long (as Pliant doesn't have line wrapping)."

That's the reason why I posted some meta in the forum of 1999/03, like "unwrap":

unwrap expression 
  duplicate ext_var 
  auto_rename 
  substitute TYPE e:0 
  substitute NAME e:1 
  substitute DLL e:2 
  substitute ID e:3

does exactly what we think...

answer posted by Hubert Tonneau on 2000/03/16 14:29:41
The 'evar.pli' will be included in release 34 main tree
as /pliant/language/compiler/misc/evar.pli module.
Also this module will not be a submodule of the general /pliant/language/compiler.pli
module, so you'll have to include it explicitely.
This is because it provides a not wildly used, but also very usefull feature.

If somebody posts a well tested 'CChar' implementation, it could also be
included.

Patrice, could you post a new 'unwrap' implementation, that does not rely
on other 'unbloc' and so on stuff, so that I can include it in release 34
as the best preposed alternative for line wrapping.
Should go in /pliant/language/basic/unwrap.pli module.
answer posted by John Eikenberry on 2000/03/16 14:31:02
When first starting with Pliant (ie. not that long ago) I tried messing around 
with unwrap, conjonction, etc. They didn't work with the new version and I didn't 
have the experience to fix them. Now that I have a bit of meta programming
experience, mabey I'll give it another whirl.

Thanks for reminding me. :)
answer posted by Patrice Ossona de Mendez on 2000/03/16 15:59:44
I propose the following that seems to work (the original version was really 
bugy!)


module "/pliant/language/compiler.pli"
# unwrap:
#
# unwrap (toto ('{}' (a b c) (d e) (f g h i) j)) -> (toto a b c d e f g h i j)
#
meta unwrap e
  if not (not e:is_pure_ident and e:size=2 and e:0:is_pure_ident and e:1:ident="{}")
    return
  var Link:Expression e2 :> (expression immediat (toto X) ident e:0:ident)
  for (var Int i) 0 e:1:size-1
    if e:1:i:is_pure_ident
      e2 insert "X" (expression immediat Ident ident e:1:i:ident near e)
      for (var Int j) 0 e:1:i:size-1
       e2 insert "X" e:1:i:j
    eif e:1:i:ident = "()"
      for (var Int j) 0 e:1:i:size-1
       e2 insert "X" e:1:i:j
    else
      e2 insert "X" e:1:i
  e2 remove "X"
  e compile_as e2
answer posted by Patrice Ossona de Mendez on 2000/03/16 15:59:56
I propose the following that seems to work (the original version was really 
bugy!)


module "/pliant/language/compiler.pli"
# unwrap:
#
# unwrap (toto ('{}' (a b c) (d e) (f g h i) j)) -> (toto a b c d e f g h i j)
#
meta unwrap e
  if not (not e:is_pure_ident and e:size=2 and e:0:is_pure_ident and e:1:ident="{}")
    return
  var Link:Expression e2 :> (expression immediat (toto X) ident e:0:ident)
  for (var Int i) 0 e:1:size-1
    if e:1:i:is_pure_ident
      e2 insert "X" (expression immediat Ident ident e:1:i:ident near e)
      for (var Int j) 0 e:1:i:size-1
       e2 insert "X" e:1:i:j
    eif e:1:i:ident = "()"
      for (var Int j) 0 e:1:i:size-1
       e2 insert "X" e:1:i:j
    else
      e2 insert "X" e:1:i
  e2 remove "X"
  e compile_as e2