Patch title: Release 85 bulk changes
Abstract:
File: /pliant/language/basic/shunt.pli
Key:
    Removed line
    Added line
# Copyright  Hubert Tonneau  hubert.tonneau@pliant.cx
#
# 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 built 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 adr :> argument local Address
  var Link:Argument result :> argument indirect type adr 0
  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 'address Universal' Universal -> Address) (e i+1):result adr)
    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 'address Universal' Universal -> Address) (e e:size-1):result adr)
  e add end
  e set_result result access_read

export shunt