'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 hubert.tonneau@heliosam.fr
#
# 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
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# 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"
abstract
['shunt' Pliant language control]
doc
['shunt' is a rather complicated control.] ; eol
[Let's assume that we are tying to compile:]
listing
shunt c1 v1 c2 v2 c3 v3 v4
para
[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.]
para
[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):]
listing
if not c1 jump part2
r := v1
jump end
part2:
if not c2 jump part3
r := v2
jump end
part3:
if not c3 jump part4
r := v3
jump end
part4:
r := v4
end:
[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
return
# the first, third, fiveth, ... must be booleans
for (var Int i) 0 e:size-3 step 2
if not (e:i cast CBool)
return
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
e:base:compile
if error_notified
return
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
|
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.
Thanks.
|
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
arguments.
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:
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'
void
Does nothing. Used mainly in order to create an instruction that
will be jumped to by another one.
function 'jump anyway'
void
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.
|