Pliant forum message

Concerning release: 33
Subject: Request
Title: Well commented meta example
initial message posted by John Eikenberry on 2000/03/08 08:48:09
I realize that this might already be planned... but I'd really like to see a 
well commented meta programming example. If one of the gurus out there would
take a little bit of time to take one of the non-trivial examples (like 
shunt) and comment the hell out it, I would be eternally grateful. I spent most
of the weekend trying to figure out meta programming with limited success.

For instance... how do I assign a value to part of the expression passed in. 
Like in this simple (artificial) example:

gvar Int value
etest value 1+2

I've figured out how to compile and get the result to the 1+2 part... but I 
can't figure out how to assign the result to the 'value' variable. 

Help with this specific problem would be of use... but I'm sure I'll have a ton
more questions and just a well documented example would help a lot.

answer posted by Hubert Tonneau on 2000/03/08 15:37:24
'shunt' is not at all the simplest possible meta programming sample.
Here is a more documented version (use the HTTP server in order to
pretty view it):

# Copyright (C) 1983 - 2000  Hubert Tonneau
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 2
# as published by the Free Software Foundation.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# version 2 along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

scope "/pliant/language/" "/pliant/install/"
module "/pliant/install/ring2.pli"

  ['shunt' Pliant language control]
  ['shunt' is a rather complicated control.] ; eol
  [Let's assume that we are tying to compile:]
    shunt c1 v1 c2 v2 c3 v3 v4
    [The hard part of it is to decide the type of the end result will be. ]
    [This is done through first testing if all v3 v3 v4 can be casted to the type of v1, ]
    [and if not, testing if all v1 v3 v4 can be casted to the type of v2, and so on. ]
    [The code for this part is the long 'while' loop.]
    [The second part, that build the set of instructions is very classical.]
    [The end result program will be (this is not a valid Pliant listing, but rather a symbolic program):]
      if not c1 jump part2
      r := v1
      jump end
      if not c2 jump part3
      r := v2
      jump end
      if not c3 jump part4
      r := v3
      jump end
      r := v4
    [Keep in mind that in the low level Pliant compiling engine, there are no more high level controls such as 'if' and 'while', only jump and conditional jump instructions.] ; eol
    [The instructions are not build sequencialy: this is very common since if an instruction needs to jump or conditional jump to another one, then it's easyer to first build the target instructions rather than setting the jump parameter in the source instruction at a later point. ]
    [On the other hand, the build instructions are added sequencialy to the expression instructions list.] ; eol
    [Lastly, each argument must be suckuped before it's result be used. 'suckup' means insert the set of instructions that compute the argument in the expression instructions list.]

meta shunt e
  # we must have at least three arguments and an odd number
  if e:size<3 or e:size%2<>1
  # the first, third, fiveth, ... must be booleans
  for (var Int i) 0 e:size-3 step 2
    if not (e:i cast CBool)
  var Int base := -1 # indice of the argument we are going to test of the final type
  var Pointer:Type type # the type of the result of the 'shunt'
  var CBool ok := false # does the type fit ?
  while not ok
    # 'base' is 1, then 3, then 5, ... , and finaly then the last argument
    # so is the indice of v1 v2 v3 v4 arguments in our example
    base := base+2
    if base>e:size
      return # the type of none of the arguments can be the result
    eif base=e:size
      base := base-1
    # test if the argument can be compile
    if error_notified
    e:base:uncast # uncast it since previous loops may have cast it
    type :> e:base:result:type # and extract the type of the argument result
    # test if v1 v2 v3 v4 can all be casted to 'type'
    ok := true
    i := 1
    while ok and i<=e:size
      if not (e:(min i e:size-1) cast type)
        ok := false # one of the arguments cannot be casted to 'type'
      i := i+2
  # so 'type' is now a type that all values can be casted to
  var Link:Instruction end :> instruction the_function:'do nothing'
  var Link:Instruction next :> instruction the_function:'do nothing'
  var Link:Argument result :> argument local type
  for (var Int i) 0 e:size-3 step 2
    e suckup e:i
    e add (instruction (the_function 'jump if not' CBool) e:i:result jump next)
    e suckup (e i+1)
    e add (instruction (the_function 'copy Universal' Universal Universal Type) (e i+1):result result (argument mapped_constant Type type))
    e add (instruction the_function:'jump anyway' jump end)
    e add next
    next :> instruction the_function:'do nothing'
  e add next
  e suckup (e e:size-1)
  e add (instruction (the_function 'copy Universal' Universal Universal Type) (e e:size-1):result result (argument mapped_constant Type type))
  e add end
  e set_result result access_read

export shunt
answer posted by John Eikenberry on 2000/03/10 04:17:12
Cool... that helped. But it raised another question/request. 

Is the 'language' the instructions use documented anywhere (if so, I can't find 
it)? I get the usage of 'jump if not', 'copy Universal', etc. Though I'm 
assuming there are more than just the ones used in shunt.pli. And it would be 
nice if all these functions were defined somewhere.

I'd also like to make the general suggestion that when the documentation starts
getting updated/fleshed-out that the meta programming section get high priority.
It is the most complex and powerful aspect of pliant and it would benefit the 
most from good documentation.

answer posted by Hubert Tonneau on 2000/03/10 09:20:45
In a meta, you can use any existing function or method in order
to create an instruction. You just need to provide the right
number of arguments, and the arguments must have the right
type because PLIANT ENGINE WILL NOT CAST OR CHECK mismatching
All the functions defined in the C part of Pliant are declared
at Pliant level in /pliant/language/startup/startup.c

These are the 'special' functions. By 'special' I don't mean that
they is anything special about their implementation, but rather
that they are mainly usefull for writing instructions in a meta

function 'create Universal' t u
  arg Type t ; arg_w Universal u

Creates the instance through first creating all it's fields, then
calling it's 'create' method. This is done automaticaly by Pliant
engine most of the time, so you will not use it very often.

function 'destroy Universal' t u
  arg Type t ; arg_w Universal u

Destroyes the instance through first calling it's 'destroy' method,
then destroying it's fields.

function 'copy Universal' src dest t
  arg Universal src ; arg_w Universal dest ; arg Type t

Copies the instance through calling it's 'copy' method or copying
it's fields if there is no 'copy' method.
The type argument is at end because when an instruction is a copy
instruction (it as a special flag function_flag_copy set), then it
is assumed that it's first argument is the source and the second
is the target. This is a very convienient assertion for
optimizing functions.

function 'address Univeral' u -> a
  arg Universal u ; arg Address a

Gets the address of the instance.

method t create_instance a
  arg Type a ; arg Address a

method t destroy_instance a
  arg Type a ; arg Address a

method t copy_instance src dest
  arg Type a ; arg Address src dest

These are basicaly the same as the previous ones, but the instances
are specifyed through their addresses.

function 'do nothing'

Does nothing. Used mainly in order to create an instruction that
will be jumped to by another one.

function 'jump anyway'

This is the same as a C goto. An instruction calling a 'jump ...'
function must point to the target instruction.

function 'jump if' c
  arg CBool c

function 'jump if not' c
  arg CBool c

Conditional jumps.