# Copyright (c) 2002-2003 by William J. Coleda

# All the macros we need to handle splitting of words into raw/cooked
# chunks.

# All the commented out print's could be replaced with .debugs()s

#
# All of this strikes me as a very good candidate for making into a PMC or an
# Object of some sort.
#

  # These are our variables. don't touch!
  .local int current_pointer
  .local int last_pointer
  .local int start_pointer
  .local int iterator

  .local PerlArray next_array
  .local PerlArray string_array
  .local PerlArray cooked_array

#
# Given a replacement string and where to put it in the current chunk,
# split the string into the appropriate sections, then fixup the linked
# list so it all still works.
#
# Once we've made the replacement, the pointer should be pointing at the
# chunk containing the middle part of our replacement, and the last_pointer
# should be fixed up appropriately. This allows the caller to 
# do the replacement and then call "next" without any finagling

# XXX This algorithm leaves the original string in the array of strings - 
# It's unreachable, and chewing memory. Perhaps someone more familiar
# parrot guts could DTRT. I don't expect this to be a concern, given the
# size of the strings that we're dealing with.

# XXX If you call with an invalid start/length combo, you may end up with
# no errors, but still have a string. Caveat caller.
 
.macro substitution_replace(STRING,START,LENGTH)

  # First, create our own new linked list, on the end of the existing one.
  .sym int temp_start
  .sym int save_temp_start
  .sym int local_current
  .sym string original_string
  .sym string temp_string
  .sym int offset
  .sym int tmp_length
  .sym int original_string_len
  .sym int save_pointer
  .sym int save_last_pointer
  .sym int looper
  .sym int save_next
  .sym int did_first

  #print "START at "
  #print .START
  #print "\nFOR "
  #print .LENGTH
  #print "\nwith "
  #print .STRING
  #print "\n"

  did_first = 0

  original_string = string_array[current_pointer]
  length original_string_len, original_string

  temp_start = string_array
  save_temp_start = temp_start
  local_current = temp_start
  dec local_current

  # If we start at >0 , then we need to first add a substr from the left of 
  # our replacement to the new list.
  save_last_pointer = last_pointer
  if .START == 0 goto .$MIDDLE
  inc local_current
  did_first = 1
  substr temp_string, original_string, 0, .START
  string_array[local_current] = temp_string
  cooked_array[local_current] = 0
  # we set next_array below

  #print "CHUNK1 is '"
  #print temp_string
  #print "'\n"
 
.local $MIDDLE:
  inc local_current
  save_pointer = local_current
  string_array[local_current] = .STRING
  cooked_array[local_current] = 1
  # we set next_array below 
  
  #print "CHUNK2 is '"
  #print .STRING
  #print "'\n"

  offset = .START + .LENGTH 
  dec offset

  if offset == original_string_len goto .$FIXUP

# END - there's a bit at the end of our string that needs rescuing.
  inc local_current

  tmp_length = original_string_len - offset
  inc tmp_length
  substr temp_string, original_string, offset, tmp_length
  string_array[local_current] = temp_string
  cooked_array[local_current] = 0
  # set next_array below 

  #print "CHUNK3 is '"
  #print temp_string
  #print "'\n"

  # Now, fixup so that the old list flows through the new list instead
  # of the old chunk.
.local $FIXUP:
  #print "FIXUP:\n"
  if last_pointer == -1 goto .$FIXUP_2
  #print "next_array["
  #print last_pointer
  #print "] = "
  #print temp_start
  #print "\n"
  next_array[last_pointer] = temp_start 
.local $FIXUP_2:
  #print "FIXUP_2:\n"
  if current_pointer != start_pointer goto .$FIXUP_3
  #print "Setting start_pointer to: "
  start_pointer = temp_start
  #print start_pointer
  #print "\n"
.local $FIXUP_3:
  #print "FIXUP_3:\n"

  #print "current_pointer is "
  #print current_pointer
  #print "\n"

  #print "last_pointer is "
  #print last_pointer
  #print "\n"

  #print "start_pointer is "
  #print start_pointer
  #print "\n"

  save_next = next_array[current_pointer]
  #print "save_next is "
  #print save_next
  #print "\n"
  # make sure we move the current pointer to the cooked_chunk
  current_pointer = save_pointer

.local $FIXUP_loop:
  #print "FIXUP_LOOP\n"
  looper = temp_start
  inc temp_start
  #print "setting index of "
  #print looper
  #print " to point to "
  #print temp_start
  #print "\n"
  next_array[looper] = temp_start

  # (== for most loops, > covers the degenerate case)
  if temp_start >= local_current goto .$FIXUP_loop_done 
  goto .$FIXUP_loop

.local $FIXUP_loop_done:
  #print "FIXUP_Loop_done\n"
  #print "setting index of "
  #print local_current
  #print " to point to save_next of "
  #print save_next
  #print "\n"

  next_array[local_current] = save_next

 # fixup the last_pointer/current_pointer
 # The current pointer should always point at the actual replacement -
 # the last pointer, however, may need to be changed to first chunk, if
 # we actually made that replacement.

 if did_first !=1 goto .$LASTP_done
 last_pointer = local_current
 dec last_pointer

.local $LASTP_done:
  noop

.endm


#
# Because we need to run different chunks of code inside the loop over
# the split up string, we'll provide ways to iterate over the chunks,
# check for done-ness, etc. Iterator-esque.
#

# Given a string, set everything up
.macro substitution_init(STRING)
  last_pointer=-1 
  start_pointer=0
  current_pointer=0
  next_array   = new PerlArray
  string_array = new PerlArray
  cooked_array = new PerlArray
 
  next_array[0]   = -1 
  string_array[0] = .STRING
  cooked_array[0] = 0
.endm

# reset iterating
.macro substitution_iter_reset()
  last_pointer=-1
  current_pointer=start_pointer
  iterator = 0
.endm

# point to the next raw chunk
# If we've tried to walk off the end of the string
# then TRIP will be set to 1 (else 0)

.macro substitution_iter_next(TRIP)
  .sym int boolean

  .TRIP = 0

  if last_pointer != -1 goto  .$LOOP
  if iterator == 1 goto .$LOOP
  iterator = 1 
  boolean = cooked_array[current_pointer] 
  if boolean == 0 goto .$DONE

.local $LOOP:
  last_pointer = current_pointer
  current_pointer = next_array[current_pointer]
  
  if current_pointer == -1 goto .$OOPS
  boolean = cooked_array[current_pointer]
  if boolean == 0 goto .$DONE
  goto .$LOOP

.local $OOPS:
  .TRIP = 1

.local $DONE:
  noop

.endm

#
# Walk over our linked list and return the integrated string
#
.macro substitution_integrate(OUTPUT)
  .sym int POINTER
  .sym string CURSTRING

  .OUTPUT = ""
  POINTER = start_pointer

.local $LOOP:
  #print "POINTER IS: "
  #print POINTER
  #print "\n"

  if POINTER == -1 goto .$DONE
  CURSTRING = string_array[POINTER]

  .OUTPUT = .OUTPUT . CURSTRING

  POINTER = next_array[POINTER]

  goto .$LOOP  

.local $DONE:
  noop
 
.endm

.macro substitution_debug()
  .sym int POINTER
  .sym string CURSTRING

  POINTER = start_pointer

.local $LOOP:
  print POINTER
  #print POINTER
  #print "\n"

  if POINTER == -1 goto .$DONE
  print ":"
  CURSTRING = string_array[POINTER]
  print CURSTRING
  POINTER = next_array[POINTER]
  print ":"
  print POINTER
  print "\n"
  goto .$LOOP  

.local $DONE:
  print "\n"
 
.endm

