// code generated by "translate.go.pss" a pep script
// http://bumble.sf.net/books/pars/tr/


// s.HasPrefix can be used instead of strings.HasPrefix
package main
import (
  "fmt"
  "bufio"  
  "strings"
  "strconv"
  "unicode"
  "io"  
  "os"
  "unicode/utf8"
)

// an alias for Println for brevity
var pr = fmt.Println

  /* a machine for parsing */
  type machine struct {
    SIZE int  // how many elements in stack/tape/marks
    eof bool
    charsRead int
    linesRead int
    escape rune 
    delimiter rune
    counter int
    work string
    stack []string
    cell int
    tape []string
    marks []string
    peep rune
    reader *bufio.Reader
  }

  // there is no special init for structures
  func newMachine(size int) *machine { 
    mm := machine{SIZE: size}
    mm.eof = false     // end of stream reached?
    mm.charsRead = 0   // how many chars already read
    mm.linesRead = 1   // how many lines already read
    mm.escape = '\\'
    mm.delimiter = '*'    // push/pop delimiter (default "*")
    mm.counter = 0        // a counter for anything
    mm.work = ""          // the workspace
    mm.stack = make([]string, 0, mm.SIZE)   // stack for parse tokens 
    mm.cell = 0                             // current tape cell
    // slices not arrays
    mm.tape = make([]string, mm.SIZE, mm.SIZE)  // a list of attribute for tokens 
    mm.marks = make([]string, mm.SIZE, mm.SIZE) // marked tape cells
    // or dont initialse peep until "parse()" calls "setInput()"
    // check! this is not so simple
    mm.reader = bufio.NewReader(os.Stdin)
    var err error
    mm.peep, _, err = mm.reader.ReadRune()
    if err == io.EOF { 
      mm.eof = true 
    } else if err != nil {
      fmt.Fprintln(os.Stderr, "error:", err)
      os.Exit(1)
    }
    return &mm
  }

  // method syntax.
  // func (v * vertex) abs() float64 { ... }
  // multiline strings are ok ?

  func (mm *machine) setInput(newInput string) {
    print("to be implemented")
  }

  // read one utf8 character from the input stream and 
  // update the machine.
  func (mm *machine) read() { 
    var err error
    if mm.eof { os.Exit(0) }
    mm.charsRead += 1
    // increment lines
    if mm.peep == '\n' { mm.linesRead += 1 }
    mm.work += string(mm.peep)
    // check!
    mm.peep, _, err = mm.reader.ReadRune()
    if err == io.EOF { 
      mm.eof = true 
    } else if err != nil {
      fmt.Fprintln(os.Stderr, "error:", err)
      os.Exit(1)
    }
  }

  // remove escape character: trivial method ?
  // check the python code for this, and the c code in machine.interp.c
  // bug. fix.
  func (mm *machine) unescapeChar(c string) {
    // if mm.work = "" { return }
    mm.work = strings.Replace(mm.work, "\\"+c, c, -1)
  }


  /*
  Perl code. Also allows multiple escape chars
  eg: unescape "+-xyz";
  # this walks the string and determines if the given char 
  # is already escaped or not # eg "abcab\cabc"
  # allow multiple chars for escape/unescape
  sub unescapeChar {
    my $self = shift;   # the machine
    my $chars = shift;  # list of chars to escape
    my $cc = "";
    my $result = "";
    my $isEscaped = $false;

    foreach $cc (split(//,$self->{"work"})) {
      if (($isEscaped == $false) && ($cc eq $self->{"escape"})) {
        $isEscaped = $true;
      } else { $isEscaped = $false; }
      # remove the last escape character (usually backslash)
      # this allows multiple chars for escaping
      if (($isEscaped == $true) && (index($chars, $cc) != -1)) {
        $result =~ s/.$//s; 
      }
      $result .= $cc; 
    }
    $self->{"work"} = $result;
  }

  */

  // add escape character : trivial
  func (mm *machine) escapeChar(c string) {
    mm.work = strings.Replace(mm.work, c, "\\"+c, -1)
  }

  /** a helper function to count trailing escapes */
  func (mm *machine) countEscapes(suffix string) int {
    count := 0
    ss := ""
    if strings.HasSuffix(mm.work, suffix) {
      ss = strings.TrimSuffix(mm.work, suffix)
    }
    for (strings.HasSuffix(ss, string(mm.escape))) { 
      ss = strings.TrimSuffix(ss, string(mm.escape))
      count++
    }
    return count
  }

  // reads the input stream until the workspace ends with the
  // given character or text, ignoring escaped characters
  func (mm *machine) until(suffix string) {
    if mm.eof { return; }
    // read at least one character
    mm.read()
    for true { 
      if mm.eof { return; }
      // we need to count the mm.Escape chars preceding suffix
      // if odd, keep reading, if even, stop
      if strings.HasSuffix(mm.work, suffix) {
        if (mm.countEscapes(suffix) % 2 == 0) { return }
      }
      mm.read()
    }
  }  

  /* increment the tape pointer (command ++) and grow the 
     tape and marks arrays if necessary */
  func (mm *machine) increment() { 
    mm.cell++
    if mm.cell >= len(mm.tape) {
      mm.tape = append(mm.tape, "")
      mm.marks = append(mm.marks, "")
      mm.SIZE++
    }
  }

  /* pop the last token from the stack into the workspace */
  func (mm *machine) pop() bool { 
    if len(mm.stack) == 0 { return false }
    // no, get last element of stack
    // a[len(a)-1]
    mm.work = mm.stack[len(mm.stack)-1] + mm.work
    // a = a[:len(a)-1]
    mm.stack = mm.stack[:len(mm.stack)-1]
    if mm.cell > 0 { mm.cell -= 1 }
    return true
  }

  // push the first token from the workspace to the stack 
  func (mm *machine) push() bool { 
    // dont increment the tape pointer on an empty push
    if mm.work == "" { return false }
    // push first token, or else whole string if no delimiter
    aa := strings.SplitN(mm.work, string(mm.delimiter), 2)
    if len(aa) == 1 {
      mm.stack = append(mm.stack, mm.work)
      mm.work = ""
    } else {
      mm.stack = append(mm.stack, aa[0]+string(mm.delimiter))
      mm.work = aa[1]
    }
    mm.increment()
    return true
  }

  // 
  func (mm *machine) printState() { 
    fmt.Printf("Stack %v Work[%s] Peep[%c] \n", mm.stack, mm.work, mm.peep)
    fmt.Printf("Acc:%v Esc:%c Delim:%c Chars:%v", 
      mm.counter, mm.escape, mm.delimiter, mm.charsRead)
    fmt.Printf(" Lines:%v Cell:%v EOF:%v \n", mm.linesRead, mm.cell, mm.eof)
    for ii, vv := range mm.tape {
      fmt.Printf("%v [%s] \n", ii, vv)
      if ii > 4 { return; }
    }
  } 

  func (mm *machine) goToMark(mark string) {
    markFound := false
    for ii := range mm.marks {
      if mm.marks[ii] == mark {
        mm.cell = ii; markFound = true; break
      }
    } 
    if markFound == false {
      fmt.Printf("badmark '%s'", mark)
      os.Exit(1)
    }
  }

  // this is where the actual parsing/compiling code should go
  // so that it can be used by other go classes/objects. Also
  // should have a stream argument.
  func (mm *machine) parse(s string) {
  } 

  /* adapt for clop and clip */
  func trimLastChar(s string) string {
    r, size := utf8.DecodeLastRuneInString(s)
    if r == utf8.RuneError && (size == 0 || size == 1) {
        size = 0
    }
    return s[:len(s)-size]
  }

  func (mm *machine) clip() {
    cc, _ := utf8.DecodeLastRuneInString(mm.work)
    mm.work = strings.TrimSuffix(mm.work, string(cc))  
  }

  func (mm *machine) clop() {
    _, size := utf8.DecodeRuneInString(mm.work) 
    mm.work = mm.work[size:]  
  }

  type fn func(rune) bool
  // eg unicode.IsLetter('x')
  /* check whether the string s only contains runes of type
     determined by the typeFn function */

  func isInClass(typeFn fn, s string) bool {
    if s == "" { return false; }
    for _, rr := range s {
      //if !unicode.IsLetter(rr) {
      if !typeFn(rr) { return false }
    }
    return true
  }

  /* range in format 'a,z' */
  func isInRange(start rune, end rune, s string) bool {
    if s == "" { return false; }
    for _, rr := range s {
      if (rr < start) || (rr > end) { return false }
    }
    return true
  }

  /* list of runes (unicode chars ) */
  func isInList(list string, s string) bool {
    // bug! logic incorrect. should be "onlyContainsAny"
    return strings.ContainsAny(s, list)
  }

func main() {
  // This size needs to be big for some applications. Eg 
  // calculating big palindromes. Really 
  // it should be dynamically allocated.
  var size = 30000
  var mm = newMachine(size);
  var restart = false; 
  // the go compiler complains when modules are imported but
  // not used, also if vars are not used.
  if restart {}; unicode.IsDigit('0'); strconv.Itoa(0);
  for !mm.eof { 
    
    /* lex block */
    for true { 
      mm.read()             /* read */
      // newlines and empty lines
      if (isInList("\n", mm.work)) {
        mm.work = ""          // clear
        mm.work += "\n"
        mm.tape[mm.cell] = mm.work  /* put */
        // no words on previous line, so this is a blank line
        mm.work = ""          // clear
        mm.work += strconv.Itoa(mm.counter) /* count */ 
        if (mm.work == "0") {
          mm.work = ""          // clear
          mm.work += "<p>\n"
          mm.tape[mm.cell] = mm.work  /* put */
          mm.work = ""          // clear
        }
        // set accumulator == 0 so that we can count words 
        // per line (and know which is the first word)
        mm.work = ""          // clear
        mm.counter = 0  /* zero */
        mm.charsRead = 0 /* nochars */
        mm.work += "space*"
        mm.push();
        break
      }
      // parse space
      if (isInList(" \t", mm.work)) {
        /* while */
        for isInList(" \t", string(mm.peep)) {
          if mm.eof { break }
          mm.read()
        }
        mm.work = ""          // clear
        mm.work += " "
        mm.tape[mm.cell] = mm.work  /* put */
        mm.work = ""          // clear
        mm.work += "space*"
        mm.push();
        break
      }
      // everything else is a word
      if (!isInClass(unicode.IsSpace, mm.work)) {
        // read word and increment word counter
        /* whilenot */
        for !isInClass(unicode.IsSpace, string(mm.peep)) {
          if mm.eof { break; }
          mm.read()
        }
        mm.counter++    /* a+ */
        // here parse image files in format <imfile.jpg> before we
        // change > < to entities.
        // --------------
        //B"<" {
        //  E".jpg>",E".jpeg>",E".png>",E".gif>" {
        //    clip; clop; put; clear;
        //    add "imagefile*"; push; .reparse
        //  }
        //}
        // here we build an <img> html tag from minimal and optional markup
        // attributes are <:corners:float:width:filename.ext>
        // this code is quite tricky. See also 
        //   pars/eg/imagetext.tohtml.pss 
        if (strings.HasPrefix(mm.work, "<") && strings.HasSuffix(mm.work, ">") && mm.work != "<>") {
          if (strings.HasSuffix(mm.work, ".png>") || strings.HasSuffix(mm.work, ".jpg>") || strings.HasSuffix(mm.work, ".jpeg>") || strings.HasSuffix(mm.work, ".bmp>") || strings.HasSuffix(mm.work, ".gif>")) {
            // an example image text format may be 
            // <:0:>>:4em:name.gif> or <:20%:name.gif> 
            // The order of the attributes is important but the attributes 
            // are optional eg: <:<<:r:20pt:name.jpg> wont work because the 
            // float attribute '<<' comes before the rounded corner attribute 'r'
            mm.clip()
            mm.clop()
            mm.tape[mm.cell] = mm.work  /* put */
            mm.work = ""          // clear
            mm.work += "<img "
            mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
            // we use swap to juggle the built html and the original
            // minimal markup text.
            // :0: is the circle image (avatar) indicator,
            if (strings.HasPrefix(mm.work, ":0:") || strings.HasPrefix(mm.work, ":O:") || strings.HasPrefix(mm.work, ":o:")) {
              mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
              mm.work += "style='border-radius:50%' "
              mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
              mm.clop()
              mm.clop()
            }
            // small rounded corners on the image
            if (strings.HasPrefix(mm.work, ":r:")) {
              mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
              mm.work += "style='border-radius:5%' "
              mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
              mm.clop()
              mm.clop()
            }
            // large rounded corners 
            if (strings.HasPrefix(mm.work, ":R:")) {
              mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
              mm.work += "style='border-radius:15%' "
              mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
              mm.clop()
              mm.clop()
            }
            // the float right indicator, it needs to come after :0:
            if (strings.HasPrefix(mm.work, ":>>:")) {
              mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
              mm.work += "class='float-right' "
              mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
              mm.clop()
              mm.clop()
              mm.clop()
            }
            // float left 
            if (strings.HasPrefix(mm.work, ":<<:")) {
              mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
              mm.work += "class='float-left' "
              mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
              mm.clop()
              mm.clop()
              mm.clop()
            }
            // centre indicator  
            if (strings.HasPrefix(mm.work, ":cc:")) {
              mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
              mm.work += "class='align-center' "
              mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
              mm.clop()
              mm.clop()
              mm.clop()
            }
            // now we have eg ":5em:name.jpg" or ":90%:name.gif" or just
            // "name.png" The ":5em:" is a image width specification or 
            // attribute and will just be passed to the HTML, latex etc
            // I will use a trick to 'split' the workspace on ':' by
            // pushing the width spec onto the stack. (We assume that the 
            // filename has no colons in it, which is a bit dodgy).
            if (strings.HasPrefix(mm.work, ":")) {
              mm.clop()
            }
            /* replace */
            mm.work = strings.Replace(mm.work, ":", "*", -1)
            
            mm.push();
            // only imagename.ext
            if (mm.work == "") {
              // build the html with imagename    
              mm.pop();
              mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
              mm.work += "src='"
              mm.work += mm.tape[mm.cell] /* get */
              mm.work += "'/>"
              mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
              mm.work = ""          // clear
            }
            // width:imagename.ext, width is on the stack
            if (mm.work != "") {
              // I pushed the width on the stack so have to realign
              // the tape pointer to where I am building the html <img> tag.
              if mm.cell > 0 { mm.cell-- }  /* -- */
              mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
              mm.work += "src='"
              mm.work += mm.tape[mm.cell] /* get */
              mm.work += "' "
              mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
              mm.increment()     /* ++ */ 
              
              mm.work = ""          // clear
              mm.pop();
              // now have eg "6em*" in workspace
              mm.clip()
              mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
              mm.work += "style='width:"
              mm.work += mm.tape[mm.cell] /* get */
              mm.work += "'/>\n"
              mm.tape[mm.cell] = mm.work  /* put */
              mm.work = ""          // clear
            }
            mm.work = ""          // clear
            mm.work += "imagefile*"
            mm.push();
            break
          }
        }
        // make < and > html entities because they will wreck our page
        // but not if is >> as 1st word
        if (mm.work != ">>") {
          /* replace */
          mm.work = strings.Replace(mm.work, ">", "&gt;", -1)
          
          /* replace */
          mm.work = strings.Replace(mm.work, "<", "&lt;", -1)
          
        }
        // some curly quotes, why not? A half hearted attempt for english
        if (mm.work == "I'm" || mm.work == "you're" || mm.work == "he's" || mm.work == "she's" || mm.work == "it's" || mm.work == "we're" || mm.work == "they're" || mm.work == "aren't" || mm.work == "can't" || mm.work == "couldn't" || mm.work == "didn't" || mm.work == "doesn't" || mm.work == "hadn't" || mm.work == "hasn't" || mm.work == "haven't" || mm.work == "isn't" || mm.work == "mightn't" || mm.work == "mustn't" || mm.work == "oughtn't" || mm.work == "shouldn't" || mm.work == "wasn't" || mm.work == "weren't" || mm.work == "won't" || mm.work == "wouldn't" || mm.work == "I've" || mm.work == "you've" || mm.work == "he's" || mm.work == "she's" || mm.work == "it's" || mm.work == "we've" || mm.work == "they've" || mm.work == "I'd" || mm.work == "you'd" || mm.work == "he'd" || mm.work == "she'd" || mm.work == "it'd" || mm.work == "we'd" || mm.work == "they'd" || mm.work == "I'll" || mm.work == "you'll" || mm.work == "he'll" || mm.work == "she'll" || mm.work == "it'll" || mm.work == "we'll" || mm.work == "they'll" || mm.work == "there's" || mm.work == "that's" || mm.work == "what's" || mm.work == "who's" || mm.work == "where's" || mm.work == "when's" || mm.work == "why's" || mm.work == "how's") {
          /* replace */
          mm.work = strings.Replace(mm.work, "'", "&rsquo;", -1)
          
        }
        // some common typos for apostrophe contractions in english
        if (mm.work == "dont" || mm.work == "Im" || mm.work == "theyre" || mm.work == "arent" || mm.work == "cant" || mm.work == "isnt" || mm.work == "couldnt") {
          /* replace */
          mm.work = strings.Replace(mm.work, "dont", "don&rsquo;t", -1)
          
          /* replace */
          mm.work = strings.Replace(mm.work, "Im", "I&rsquo;m", -1)
          
          /* replace */
          mm.work = strings.Replace(mm.work, "cant", "can&rsquo;t", -1)
          
          /* replace */
          mm.work = strings.Replace(mm.work, "arent", "aren&rsquo;t", -1)
          
        }
        mm.tape[mm.cell] = mm.work  /* put */
        // ordinals in english, very perfunctory but sort of fun. 
        // eg: 1st, 2nd, 301rd
        if (isInList("0123456789stndrdth", mm.work)) {
          if (strings.HasSuffix(mm.work, "1st")) {
            // check matches [0-9]*1st
            mm.clip()
            mm.clip()
            mm.clip()
            if (mm.work == "" || isInRange('0','9', mm.work)) {
              mm.work = ""          // clear
              mm.work += mm.tape[mm.cell] /* get */
              mm.clip()
              mm.clip()
              mm.work += "<sup>st</sup>"
            }
          }
          if (strings.HasSuffix(mm.work, "2nd")) {
            // check matches [0-9]*2nd
            mm.clip()
            mm.clip()
            mm.clip()
            if (mm.work == "" || isInRange('0','9', mm.work)) {
              mm.work = ""          // clear
              mm.work += mm.tape[mm.cell] /* get */
              mm.clip()
              mm.clip()
              mm.work += "<sup>nd</sup>"
            }
          }
          if (strings.HasSuffix(mm.work, "3rd")) {
            mm.clip()
            mm.clip()
            mm.clip()
            if (mm.work == "" || isInRange('0','9', mm.work)) {
              mm.work = ""          // clear
              mm.work += mm.tape[mm.cell] /* get */
              mm.clip()
              mm.clip()
              mm.work += "<sup>rd</sup>"
            }
          }
          if (strings.HasSuffix(mm.work, "th")) {
            // check matches [0-9]*[4-9]th 
            mm.clip()
            mm.clip()
            if (mm.work != "" && !strings.HasSuffix(mm.work,"1") && !strings.HasSuffix(mm.work,"2") && !strings.HasSuffix(mm.work,"3") && isInRange('0','9', mm.work)) {
              mm.work += "<sup>th</sup>"
            }
          }
        }
        mm.tape[mm.cell] = mm.work  /* put */
        mm.work = ""          // clear
        mm.work += strconv.Itoa(mm.counter) /* count */ 
        // deal with ">>" when not first word
        if (mm.work != "1") {
          mm.work = ""          // clear
          mm.work += mm.tape[mm.cell] /* get */
          if (mm.work == ">>") {
            mm.work = ""          // clear
            mm.work += "&gt;&gt;"
            mm.tape[mm.cell] = mm.work  /* put */
          }
          mm.work = ""          // clear
          mm.work += strconv.Itoa(mm.counter) /* count */ 
        }
        // check if this is the first word on the line
        // because several markup elements (as in markdown) need to be
        // the 1st word to be significant.
        if (mm.work == "1") {
          mm.work = ""          // clear
          mm.work += mm.tape[mm.cell] /* get */
          // asterix as first word on line marks the description of 
          // a code line or block which follows (like a caption)
          if (mm.work == "*") {
            mm.work = ""          // clear
            /* whilenot */
            for !isInList("\n", string(mm.peep)) {
              if mm.eof { break; }
              mm.read()
            }
            /* replace */
            mm.work = strings.Replace(mm.work, ">", "&gt;", -1)
            
            /* replace */
            mm.work = strings.Replace(mm.work, "<", "&lt;", -1)
            
            mm.tape[mm.cell] = mm.work  /* put */
            mm.work = ""          // clear
            mm.work += "<em class='starline'>\n"
            mm.work += mm.tape[mm.cell] /* get */
            mm.work += "\n</em>\n"
            mm.tape[mm.cell] = mm.work  /* put */
            mm.work = ""          // clear
            mm.work += "starline*"
            mm.push();
            break
          }
          // markdown style headings
          if (mm.work == "#") {
            mm.work = ""          // clear
            /* whilenot */
            for !isInList("\n", string(mm.peep)) {
              if mm.eof { break; }
              mm.read()
            }
            /* replace */
            mm.work = strings.Replace(mm.work, ">", "&gt;", -1)
            
            /* replace */
            mm.work = strings.Replace(mm.work, "<", "&lt;", -1)
            
            mm.tape[mm.cell] = mm.work  /* put */
            mm.work = ""          // clear
            mm.work += "<!-- ------------------------------- -->\n"
            mm.work += "<h1>"
            mm.work += mm.tape[mm.cell] /* get */
            mm.work += "</h1>\n"
            mm.tape[mm.cell] = mm.work  /* put */
            mm.work = ""          // clear
            mm.work += "text*"
            mm.push();
            break
          }
          if (mm.work == "##") {
            mm.work = ""          // clear
            /* whilenot */
            for !isInList("\n", string(mm.peep)) {
              if mm.eof { break; }
              mm.read()
            }
            /* replace */
            mm.work = strings.Replace(mm.work, ">", "&gt;", -1)
            
            /* replace */
            mm.work = strings.Replace(mm.work, "<", "&lt;", -1)
            
            mm.tape[mm.cell] = mm.work  /* put */
            mm.work = ""          // clear
            mm.work += "<!-- ------------------------------- -->\n"
            mm.work += "<h2>"
            mm.work += mm.tape[mm.cell] /* get */
            mm.work += "</h2>\n"
            mm.tape[mm.cell] = mm.work  /* put */
            mm.work = ""          // clear
            mm.work += "text*"
            mm.push();
            break
          }
          if (mm.work == "###") {
            mm.work = ""          // clear
            /* whilenot */
            for !isInList("\n", string(mm.peep)) {
              if mm.eof { break; }
              mm.read()
            }
            /* replace */
            mm.work = strings.Replace(mm.work, ">", "&gt;", -1)
            
            /* replace */
            mm.work = strings.Replace(mm.work, "<", "&lt;", -1)
            
            mm.tape[mm.cell] = mm.work  /* put */
            mm.work = ""          // clear
            mm.work += "<!-- ------------------------------- -->\n"
            mm.work += "<h3>"
            mm.work += mm.tape[mm.cell] /* get */
            mm.work += "</h3>\n"
            mm.tape[mm.cell] = mm.work  /* put */
            mm.work = ""          // clear
            mm.work += "text*"
            mm.push();
            break
          }
          // one line of code etc
          if (mm.work == ">>") {
            mm.work = ""          // clear
            /* whilenot */
            for !isInList("\n", string(mm.peep)) {
              if mm.eof { break; }
              mm.read()
            }
            /* replace */
            mm.work = strings.Replace(mm.work, ">", "&gt;", -1)
            
            /* replace */
            mm.work = strings.Replace(mm.work, "<", "&lt;", -1)
            
            mm.tape[mm.cell] = mm.work  /* put */
            mm.work = ""          // clear
            mm.work += "<pre class='codeline'>\n"
            mm.work += mm.tape[mm.cell] /* get */
            mm.work += "\n</pre>\n"
            mm.tape[mm.cell] = mm.work  /* put */
            mm.work = ""          // clear
            mm.work += "codeline*"
            mm.push();
            break
          }
          // horizontal rules >--------  (> is already &gt;)
          if (strings.HasPrefix(mm.work, "&gt;---")) {
            // ensure matches regex ">[-]{3,}"
            mm.clop()
            mm.clop()
            mm.clop()
            mm.clop()
            if (isInList("-", mm.work)) {
              mm.work = ""          // clear
              mm.work += "<hr/>\n"
              mm.tape[mm.cell] = mm.work  /* put */
              mm.work += "text*"
              mm.push();
              break
            }
          }
          // codeblocks begin with --- or ---- etc
          if (strings.HasPrefix(mm.work, "---") && isInList("-", mm.work)) {
            mm.work = ""          // clear
            mm.until(",,,");
            mm.clip()
            mm.clip()
            mm.clip()
            /* replace */
            mm.work = strings.Replace(mm.work, ">", "&gt;", -1)
            
            /* replace */
            mm.work = strings.Replace(mm.work, "<", "&lt;", -1)
            
            mm.tape[mm.cell] = mm.work  /* put */
            mm.work = ""          // clear
            mm.work += "<pre class='codeblock'>\n"
            mm.work += mm.tape[mm.cell] /* get */
            mm.work += "</pre>\n"
            mm.tape[mm.cell] = mm.work  /* put */
            mm.work = ""          // clear
            /* while */
            for isInList(",", string(mm.peep)) {
              if mm.eof { break }
              mm.read()
            }
            mm.work = ""          // clear
            mm.work += "codeblock*"
            mm.push();
            break
          }
        }
        mm.work = ""          // clear
        mm.work += mm.tape[mm.cell] /* get */
        // force a line break with '>>' (but not first word on line), 
        // could be a way to imitate lists
        if (mm.work == "&gt;&gt;") {
          mm.work = ""          // clear
          mm.work += "<br/>\n"
          mm.tape[mm.cell] = mm.work  /* put */
          mm.work += "text*"
          mm.push();
          break
        }
        // urls, we need to add html formatting later because of the
        // "text" http://dada.org syntax
        if (strings.HasPrefix(mm.work, "wp:") || strings.HasPrefix(mm.work, "http://") || strings.HasPrefix(mm.work, "https://") || strings.HasPrefix(mm.work, "nntp://") || strings.HasPrefix(mm.work, "file://") || strings.HasPrefix(mm.work, "www.")) {
          if (mm.work != "wp:" && mm.work != "http://" && mm.work != "https://" && mm.work != "nntp://" && mm.work != "file://" && mm.work != "www.") {
            // make the fake schema wp:// or wp: wikipedia links after wp:// should
            // just be the wikipedia page name
            if (strings.HasPrefix(mm.work, "wp://")) {
              mm.clop()
              mm.clop()
              mm.clop()
              mm.clop()
              mm.clop()
              mm.tape[mm.cell] = mm.work  /* put */
              mm.work = ""          // clear
              mm.work += "https://en.wikipedia.org/wiki/"
              mm.work += mm.tape[mm.cell] /* get */
              mm.tape[mm.cell] = mm.work  /* put */
            }
            if (strings.HasPrefix(mm.work, "wp:")) {
              mm.clop()
              mm.clop()
              mm.clop()
              mm.tape[mm.cell] = mm.work  /* put */
              mm.work = ""          // clear
              mm.work += "https://en.wikipedia.org/wiki/"
              mm.work += mm.tape[mm.cell] /* get */
              mm.tape[mm.cell] = mm.work  /* put */
            }
            // another fake schema, this time for pep/nom commands 
            // eg: push pop get put
            if (strings.HasPrefix(mm.work, "nom:") || strings.HasPrefix(mm.work, "nom://")) {
              if (strings.HasPrefix(mm.work, "nom://")) {
                /* replace */
                mm.work = strings.Replace(mm.work, "nom://", "", -1)
                
              }
              if (strings.HasPrefix(mm.work, "nom:")) {
                /* replace */
                mm.work = strings.Replace(mm.work, "nom:", "", -1)
                
              }
              mm.work = ""          // clear
              mm.work += "http://peptool.org/doc/commands/doc.nom."
              mm.work += mm.tape[mm.cell] /* get */
              mm.work += ".html"
              mm.tape[mm.cell] = mm.work  /* put */
            }
            // add a schema to www. urls
            if (strings.HasPrefix(mm.work, "www.")) {
              mm.work = ""          // clear
              mm.work += "http://"
              mm.work += mm.tape[mm.cell] /* get */
              mm.tape[mm.cell] = mm.work  /* put */
            }
            mm.work = ""          // clear
            mm.work += "url*"
            mm.push();
            break
          }
        }
        // a fake uri schema syntax eg google:"pratt parsers"
        // --> https://www.google.com/search?q=distance+colombia+to+tasmania
        // this is separate to the code above because it has to read ahead
        // in the input stream
        if (strings.HasPrefix(mm.work, "google:") || strings.HasPrefix(mm.work, "google://")) {
          /* replace */
          mm.work = strings.Replace(mm.work, "google://", "", -1)
          
          /* replace */
          mm.work = strings.Replace(mm.work, "google:", "", -1)
          
          // read until next " or newline
          if (strings.HasPrefix(mm.work, "\"")) {
            mm.clop()
            /* whilenot */
            for !isInList("\n\"", string(mm.peep)) {
              if mm.eof { break; }
              mm.read()
            }
            //replace ">" "&gt;"; replace "<" "&lt;";
            /* replace */
            mm.work = strings.Replace(mm.work, " ", "+", -1)
            
            mm.tape[mm.cell] = mm.work  /* put */
            mm.work = ""          // clear
            mm.work += "https://www.google.com/search?q="
            mm.work += mm.tape[mm.cell] /* get */
            mm.tape[mm.cell] = mm.work  /* put */
            mm.work = ""          // clear
            if (!mm.eof) {
              mm.read()             /* read */
              if (isInList("\n", mm.work)) {
                mm.counter = 0  /* zero */
                mm.charsRead = 0 /* nochars */
              }
            }
            mm.work = ""          // clear
            mm.work += "url*"
            mm.push();
            break
          }
        }
        // local files with no schema, imagefile tokens have already been parsed
        if (strings.HasSuffix(mm.work, ".h") || strings.HasSuffix(mm.work, ".c") || strings.HasSuffix(mm.work, ".a") || strings.HasSuffix(mm.work, ".txt") || strings.HasSuffix(mm.work, ".doc") || strings.HasSuffix(mm.work, ".py") || strings.HasSuffix(mm.work, ".rb") || strings.HasSuffix(mm.work, ".rs") || strings.HasSuffix(mm.work, ".java") || strings.HasSuffix(mm.work, ".class") || strings.HasSuffix(mm.work, ".tcl") || strings.HasSuffix(mm.work, ".tk") || strings.HasSuffix(mm.work, ".sw") || strings.HasSuffix(mm.work, ".js") || strings.HasSuffix(mm.work, ".go") || strings.HasSuffix(mm.work, ".pp") || strings.HasSuffix(mm.work, ".pss") || strings.HasSuffix(mm.work, ".cpp") || strings.HasSuffix(mm.work, ".pl") || strings.HasSuffix(mm.work, ".html") || strings.HasSuffix(mm.work, ".pdf") || strings.HasSuffix(mm.work, ".tex") || strings.HasSuffix(mm.work, ".sh") || strings.HasSuffix(mm.work, ".css") || strings.HasSuffix(mm.work, ".out") || strings.HasSuffix(mm.work, ".log") || strings.HasSuffix(mm.work, ".png") || strings.HasSuffix(mm.work, ".jpg") || strings.HasSuffix(mm.work, ".jpeg") || strings.HasSuffix(mm.work, ".bmp") || strings.HasSuffix(mm.work, ".mp3") || strings.HasSuffix(mm.work, ".wav") || strings.HasSuffix(mm.work, ".aux") || strings.HasSuffix(mm.work, ".tar") || strings.HasSuffix(mm.work, ".gz") || strings.HasSuffix(mm.work, "/")) {
          if (mm.work != ".h" || mm.work != ".c" || mm.work != ".a" || mm.work != ".txt" || mm.work != ".doc" || mm.work != ".py" || mm.work != ".rb" || mm.work != ".rs" || mm.work != ".java" || mm.work != ".class" || mm.work != ".tcl" || mm.work != ".tk" || mm.work != ".sw" || mm.work != ".js" || mm.work != ".go" || mm.work != ".pp" || mm.work != ".pss" || mm.work != ".cpp" || mm.work != ".pl" || mm.work != ".html" || mm.work != ".pdf" || mm.work != ".tex" || mm.work != ".sh" || mm.work != ".css" || mm.work != ".out" || mm.work != ".log" || mm.work != ".png" || mm.work != ".jpg" || mm.work != ".jpeg" || mm.work != ".bmp" || mm.work != ".mp3" || mm.work != ".wav" || mm.work != ".aux" || mm.work != ".tar" || mm.work != ".gz" || mm.work != "/") {
            if (!strings.HasPrefix(mm.work,"http://") && !strings.HasPrefix(mm.work,"https://") && !strings.HasPrefix(mm.work,"nntp://") && !strings.HasPrefix(mm.work,"file://") && !strings.HasPrefix(mm.work,"www.")) {
              mm.work = ""          // clear
              mm.work += "file*"
              mm.push();
              break
            }
          }
        }
        // quoted text between "and and", maximum one line
        if (strings.HasPrefix(mm.work, "\"") && mm.work != "\"" && !strings.HasSuffix(mm.work,"\"")) {
          mm.clop()
          /* whilenot */
          for !isInList("\n\"", string(mm.peep)) {
            if mm.eof { break; }
            mm.read()
          }
          /* replace */
          mm.work = strings.Replace(mm.work, ">", "&gt;", -1)
          
          /* replace */
          mm.work = strings.Replace(mm.work, "<", "&lt;", -1)
          
          mm.tape[mm.cell] = mm.work  /* put */
          mm.work = ""          // clear
          // The code below is not great, but is required because we
          // dont have "until 'ab','cd','ef'" syntax. ie multiple end delimiters
          // all this is to prevent multiline quotes (which could eat up the 
          // whole document.
          if (!mm.eof) {
            mm.read()             /* read */
            if (isInList("\n", mm.work)) {
              mm.counter = 0  /* zero */
              mm.charsRead = 0 /* nochars */
            }
          }
          mm.work = ""          // clear
          mm.work += "quoted*"
          mm.push();
          break
        }
        // single quoted word
        if (strings.HasPrefix(mm.work, "\"") && mm.work != "\"" && mm.work != "\"\"" && strings.HasSuffix(mm.work, "\"")) {
          mm.clip()
          mm.clop()
          /* replace */
          mm.work = strings.Replace(mm.work, ">", "&gt;", -1)
          
          /* replace */
          mm.work = strings.Replace(mm.work, "<", "&lt;", -1)
          
          mm.tape[mm.cell] = mm.work  /* put */
          mm.work = ""          // clear
          mm.work += "quoted*"
          mm.push();
          break
        }
        // single bold emphasised word eg: **strong**
        if (strings.HasPrefix(mm.work, "**") && mm.work != "**" && mm.work != "****" && mm.work != "***" && strings.HasSuffix(mm.work, "**")) {
          mm.clip()
          mm.clip()
          mm.clop()
          mm.clop()
          /* replace */
          mm.work = strings.Replace(mm.work, ">", "&gt;", -1)
          
          /* replace */
          mm.work = strings.Replace(mm.work, "<", "&lt;", -1)
          
          mm.tape[mm.cell] = mm.work  /* put */
          mm.work = ""          // clear
          mm.work += "<strong><em>"
          mm.work += mm.tape[mm.cell] /* get */
          mm.work += "</em></strong>\n"
          mm.tape[mm.cell] = mm.work  /* put */
          mm.work = ""          // clear
          mm.work += "text*"
          mm.push();
          break
        }
        // bold emphasised text between **double asterixes**
        // single line maximum, multiple words
        if (strings.HasPrefix(mm.work, "**")) {
          mm.clop()
          mm.clop()
          /* whilenot */
          for !isInList("\n*", string(mm.peep)) {
            if mm.eof { break; }
            mm.read()
          }
          // find the next * if its there. This is clumsy code because we
          // cant say "until '**','\n';" which would be better
          // actually this code accepts ** text* with only one terminating 
          // asterix, but its not important. It's a text format...
          /* replace */
          mm.work = strings.Replace(mm.work, ">", "&gt;", -1)
          
          /* replace */
          mm.work = strings.Replace(mm.work, "<", "&lt;", -1)
          
          mm.tape[mm.cell] = mm.work  /* put */
          mm.work = ""          // clear
          mm.work += "<strong><em>"
          mm.work += mm.tape[mm.cell] /* get */
          mm.work += "</em></strong>\n"
          mm.tape[mm.cell] = mm.work  /* put */
          mm.work = ""          // clear
          // If there is some emphasised text immediately on the next line
          // this will not be good, but we aren't flying an aeroplane.
          /* while */
          for isInList("*", string(mm.peep)) {
            if mm.eof { break }
            mm.read()
          }
          mm.work = ""          // clear
          mm.work += "text*"
          mm.push();
          break
        }
        // emphasised italic text between *two asterixes*
        // single line maximum, multiple words
        if (strings.HasPrefix(mm.work, "*") && mm.work != "*" && !strings.HasSuffix(mm.work,"*")) {
          mm.clop()
          /* whilenot */
          for !isInList("\n*", string(mm.peep)) {
            if mm.eof { break; }
            mm.read()
          }
          /* replace */
          mm.work = strings.Replace(mm.work, ">", "&gt;", -1)
          
          /* replace */
          mm.work = strings.Replace(mm.work, "<", "&lt;", -1)
          
          mm.tape[mm.cell] = mm.work  /* put */
          mm.work = ""          // clear
          mm.work += "<em>"
          mm.work += mm.tape[mm.cell] /* get */
          mm.work += "</em>"
          mm.tape[mm.cell] = mm.work  /* put */
          mm.work = ""          // clear
          // could i just use "while [*];" here?
          if (!mm.eof) {
            mm.read()             /* read */
            if (isInList("\n", mm.work)) {
              mm.counter = 0  /* zero */
              mm.charsRead = 0 /* nochars */
            }
          }
          mm.work = ""          // clear
          mm.work += "text*"
          mm.push();
          break
        }
        // single emphasised word, no special grammar token needed.
        if (strings.HasPrefix(mm.work, "*") && mm.work != "*" && mm.work != "**" && strings.HasSuffix(mm.work, "*")) {
          mm.clip()
          mm.clop()
          /* replace */
          mm.work = strings.Replace(mm.work, ">", "&gt;", -1)
          
          /* replace */
          mm.work = strings.Replace(mm.work, "<", "&lt;", -1)
          
          mm.tape[mm.cell] = mm.work  /* put */
          mm.work = ""          // clear
          mm.work += "<em>"
          mm.work += mm.tape[mm.cell] /* get */
          mm.work += "</em>"
          mm.tape[mm.cell] = mm.work  /* put */
          mm.work = ""          // clear
          mm.work += "text*"
          mm.push();
          break
        }
        mm.work = ""          // clear
        mm.work += "word*"
        mm.push();
      }
      if (mm.work != "") {
        mm.work = ""          // clear
        // just delete weird characters, we don't need them.
        // but probably should investigate further
      }
      break 
    }
    if restart { restart = false; continue; }
    // parse block 
    for true {
      // for debugging, add % as a latex comment.
      // add "<!-- line "; lines; add " char "; chars; add ": "; print; clear; 
      // unstack; print; stack; add " -->\n"; print; clear;
      // maximum 2 tokens
      mm.pop();
      mm.pop();
      // starline*codeline* or starline*codeblock* is significant
      if (mm.work == "starline*space*") {
        // dont really need this space
        mm.work = ""          // clear
        mm.work += mm.tape[mm.cell] /* get */
        mm.increment()     /* ++ */ 
        
        mm.work += mm.tape[mm.cell] /* get */
        if mm.cell > 0 { mm.cell-- }  /* -- */
        mm.tape[mm.cell] = mm.work  /* put */
        mm.work = ""          // clear
        mm.work += "starline*"
        mm.push();
        continue
      }
      // a caption followed by some code
      if (strings.HasPrefix(mm.work, "starline*") && mm.work != "starline*") {
        if (strings.HasSuffix(mm.work, "codeline*") || strings.HasSuffix(mm.work, "codeblock*")) {
          mm.work = ""          // clear
          mm.work += "<figure class='code-with-caption'>\n"
          mm.work += "<figcaption class='code-caption'>\n"
          mm.work += mm.tape[mm.cell] /* get */
          mm.work += "</figcaption>\n"
          mm.increment()     /* ++ */ 
          
          mm.work += mm.tape[mm.cell] /* get */
          if mm.cell > 0 { mm.cell-- }  /* -- */
          mm.work += "</figure>"
          mm.tape[mm.cell] = mm.work  /* put */
          mm.work = ""          // clear
          mm.work += "text*"
          mm.push();
          continue
        }
        // no further need for the starline token, already formatted. 
        /* replace */
        mm.work = strings.Replace(mm.work, "starline*", "text*", -1)
        
        mm.push();
        mm.push();
        continue
      }
      // format and reduce image files, but the <img> tag has already
      // been built above so we can just add the figure and caption if 
      // required
      if (strings.HasSuffix(mm.work, "imagefile*") && mm.work != "imagefile*") {
        // "link text" http://abc syntax
        if (strings.HasPrefix(mm.work, "quoted*")) {
          // the problem here is that <figure> needs to set the width 
          // and alignment not the image, but the <img> tag has already
          // been built. Maybe we can just accept that captioned images
          // are not going to look much good.
          mm.work = ""          // clear
          mm.work += "\n<figure class='float-right'>\n  "
          mm.increment()     /* ++ */ 
          
          mm.work += mm.tape[mm.cell] /* get */
          if mm.cell > 0 { mm.cell-- }  /* -- */
          mm.work += "\n  <figcaption class='image-caption'>\n  "
          mm.work += mm.tape[mm.cell] /* get */
          mm.work += "\n  </figcaption>"
          mm.work += "\n</figure>\n"
          mm.tape[mm.cell] = mm.work  /* put */
          mm.work = ""          // clear
          mm.work += "text*"
          mm.push();
          continue
        }
        // image with no caption 
        mm.work = ""          // clear
        mm.work += mm.tape[mm.cell] /* get */
        mm.increment()     /* ++ */ 
        
        mm.work += mm.tape[mm.cell] /* get */
        if mm.cell > 0 { mm.cell-- }  /* -- */
        mm.work += "\n"
        mm.tape[mm.cell] = mm.work  /* put */
        mm.work = ""          // clear
        mm.work += "text*"
        mm.push();
        continue
      }
      // format and reduce urls
      if (strings.HasSuffix(mm.work, "url*")) {
        // "link text" http://abc syntax
        if (strings.HasPrefix(mm.work, "quoted*")) {
          mm.work = ""          // clear
          mm.work += "<a href='"
          mm.increment()     /* ++ */ 
          
          mm.work += mm.tape[mm.cell] /* get */
          if mm.cell > 0 { mm.cell-- }  /* -- */
          mm.work += "'>"
          mm.work += mm.tape[mm.cell] /* get */
          mm.work += "</a>"
          mm.tape[mm.cell] = mm.work  /* put */
          mm.work = ""          // clear
          mm.work += "text*"
          mm.push();
          continue
        }
        // plain url link, add html link to text
        mm.work = ""          // clear
        mm.work += mm.tape[mm.cell] /* get */
        mm.work += "<a href='"
        mm.increment()     /* ++ */ 
        
        mm.work += mm.tape[mm.cell] /* get */
        mm.work += "'>"
        // remove the https:// etc from the visible link because
        // they look ugly in the text.
        mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
        /* replace */
        mm.work = strings.Replace(mm.work, "https", "", -1)
        
        /* replace */
        mm.work = strings.Replace(mm.work, "http", "", -1)
        
        /* replace */
        mm.work = strings.Replace(mm.work, "nntp", "", -1)
        
        /* replace */
        mm.work = strings.Replace(mm.work, "file", "", -1)
        
        /* replace */
        mm.work = strings.Replace(mm.work, "://", "", -1)
        
        mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work  /* swap */
        mm.work += mm.tape[mm.cell] /* get */
        // get; 
        if mm.cell > 0 { mm.cell-- }  /* -- */
        mm.work += "</a>"
        mm.tape[mm.cell] = mm.work  /* put */
        mm.work = ""          // clear
        mm.work += "text*"
        mm.push();
        continue
      }
      // "text" file.txt syntax to be linked
      if (mm.work == "quoted*file*") {
        mm.work = ""          // clear
        mm.work += "<a href='"
        mm.increment()     /* ++ */ 
        
        mm.work += mm.tape[mm.cell] /* get */
        if mm.cell > 0 { mm.cell-- }  /* -- */
        mm.work += "'>"
        mm.work += mm.tape[mm.cell] /* get */
        mm.work += "</a>"
        mm.tape[mm.cell] = mm.work  /* put */
        mm.work = ""          // clear
        mm.work += "text*"
        mm.push();
        continue
      }
      // reduce file* grammar tokens separately so we can html format them
      if (strings.HasPrefix(mm.work, "file*") && mm.work != "file*") {
        /* replace */
        mm.work = strings.Replace(mm.work, "file*", "text*", -1)
        
        mm.push();
        mm.push();
        if mm.cell > 0 { mm.cell-- }  /* -- */
        if mm.cell > 0 { mm.cell-- }  /* -- */
        mm.work += "<code>"
        mm.work += mm.tape[mm.cell] /* get */
        mm.work += "</code>"
        mm.tape[mm.cell] = mm.work  /* put */
        mm.increment()     /* ++ */ 
        
        mm.increment()     /* ++ */ 
        
        mm.work = ""          // clear
        continue
      }
      // quoted*url* or quoted*file* is significant
      if (mm.work == "quoted*space*") {
        mm.work = ""          // clear
        mm.work += mm.tape[mm.cell] /* get */
        mm.increment()     /* ++ */ 
        
        mm.work += mm.tape[mm.cell] /* get */
        if mm.cell > 0 { mm.cell-- }  /* -- */
        mm.tape[mm.cell] = mm.work  /* put */
        mm.work = ""          // clear
        mm.work += "quoted*"
        mm.push();
        continue
      }
      // reduce "quoted" separately so we can add some html curly quotes
      // the !"quoted*" clause is supposed to ensure 2 tokens (this should only
      // really be a problem if the "quoted" is the first word of the document)
      if (strings.HasPrefix(mm.work, "quoted*") && mm.work != "quoted*") {
        if (!strings.HasSuffix(mm.work,"url*") && !strings.HasSuffix(mm.work,"file*")) {
          mm.work = ""          // clear
          // The quoted attribute may have a space (or many?) at the end
          // so need to put it after the curly quotes
          // add some html curly quotes and get saved space
          mm.work += "&ldquo;"
          mm.work += mm.tape[mm.cell] /* get */
          mm.work += "&rdquo;"
          // remove the space just before the last quote (which is added
          // during "space*" reductions.
          /* replace */
          mm.work = strings.Replace(mm.work, " &rdquo;", "&rdquo;", -1)
          
          // add a space to separate from next word.
          mm.work += " "
          mm.increment()     /* ++ */ 
          
          mm.work += mm.tape[mm.cell] /* get */
          if mm.cell > 0 { mm.cell-- }  /* -- */
          mm.tape[mm.cell] = mm.work  /* put */
          mm.work = ""          // clear
          mm.work += "text*"
          mm.push();
          continue
        }
      }
      // tokens to reduce to text
      // codeline, codeblock, word, text, space, quoted,
      //B"word*",B"text*",B"space*",B"quoted*",B"codeline*",B"codeblock*" {
      if (strings.HasPrefix(mm.work, "word*") || strings.HasPrefix(mm.work, "text*") || strings.HasPrefix(mm.work, "space*") || strings.HasPrefix(mm.work, "codeline*") || strings.HasPrefix(mm.work, "codeblock*")) {
        // need to conserve quoted at end
        if (strings.HasSuffix(mm.work, "word*") || strings.HasSuffix(mm.work, "text*") || strings.HasSuffix(mm.work, "space*") || strings.HasSuffix(mm.work, "codeline*") || strings.HasSuffix(mm.work, "codeblock*")) {
          // check that there really are 2 tokens (not one)
          mm.push();
          if (mm.work != "") {
            mm.pop();
            mm.work = ""          // clear
            mm.work += mm.tape[mm.cell] /* get */
            mm.increment()     /* ++ */ 
            
            mm.work += mm.tape[mm.cell] /* get */
            if mm.cell > 0 { mm.cell-- }  /* -- */
            mm.tape[mm.cell] = mm.work  /* put */
            mm.work = ""          // clear
            mm.work += "text*"
            mm.push();
            continue
          }
          mm.pop();
        }
      }
      mm.push();
      mm.push();
      if (mm.eof) {
        mm.work += "<!-- final stack: "
        fmt.Printf("%s", mm.work)    // print
        mm.work = ""          // clear
        for mm.pop() {}   /* unstack */ 
        mm.work += " -->\n"
        fmt.Printf("%s", mm.work)    // print
        mm.work = ""          // clear
        for mm.push() {}  /* stack */
        mm.pop();
        // print the rendered html
        mm.work = ""          // clear
        mm.work += mm.tape[mm.cell] /* get */
        // terminating the html (with a footer) can be handled
        // by the bash helper function eg in pars/www/nomblog.site/helpers.nomblog.sh
        fmt.Printf("%s", mm.work)    // print
        os.Exit(0)
      }
      break 
    } // parse
    
  }
}


// end of generated 'go' code