// 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);
  // the empty recordset trick to simplify the grammar rules
mm.work += "recordset*"
mm.push();
for !mm.eof { 
    
    /* lex block */
    for true { 
      mm.read()             /* read */
      if (isInList("\n", mm.work)) {
        // just to debug
        // lines; print;
        mm.work = ""          // clear
      }
      /* whilenot */
      for !isInList("\n", string(mm.peep)) {
        if mm.eof { break; }
        mm.read()
      }
      // ignore blank lines
      if (mm.work == "" || isInClass(unicode.IsSpace, mm.work)) {
        mm.work = ""          // clear
        break
      }
      mm.tape[mm.cell] = mm.work  /* put */
      if (strings.HasPrefix(mm.work, "#") && mm.work != "#") {
        if (isInList("#0123456789", mm.work)) {
          mm.work = ""          // clear
          mm.work += "timestamp*"
          mm.push();
          break
        }
        mm.work = ""          // clear
        mm.work += "comment*"
        mm.push();
        break
      }
      // tag the command as trivial if it is 
      // for later removal. If there is a comment above it we may keep it anyway
      // tag as trivial all commands less than 5 characters
      mm.clip()
      mm.clip()
      mm.clip()
      mm.clip()
      if (mm.work == "") {
        mm.work = ""          // clear
        mm.work += "trivial*"
        mm.push();
        break
      }
      mm.work = ""          // clear
      mm.work += mm.tape[mm.cell] /* get */
      if (strings.HasPrefix(mm.work, "blog.") || strings.HasPrefix(mm.work, "aa.") || strings.HasPrefix(mm.work, "ardu.") || strings.HasPrefix(mm.work, "df ") || mm.work == "df" || strings.HasPrefix(mm.work, "du ") || strings.HasPrefix(mm.work, "mv ") || strings.HasPrefix(mm.work, "cp ") || strings.HasPrefix(mm.work, "less ") || strings.HasPrefix(mm.work, "vim ") || strings.HasPrefix(mm.work, "rm ") || strings.HasPrefix(mm.work, "mkdir ") || strings.HasPrefix(mm.work, "find ") || strings.HasPrefix(mm.work, "locate ") || strings.HasPrefix(mm.work, "cd ") || mm.work == "cd" || strings.HasPrefix(mm.work, "ls ") || mm.work == "ls" || mm.work == "pwd" || mm.work == "hist" || mm.work == "books" || mm.work == "bk" || mm.work == "ho" || mm.work == "updatedb" || mm.work == "bashrc" || mm.work == "vimrc" || mm.work == "os" || mm.work == "cos" || mm.work == "ccos" || mm.work == "make") {
        mm.work = ""          // clear
        mm.work += "trivial*"
        mm.push();
        break
      }
      mm.work = ""          // clear
      mm.work += "command*"
      mm.push();
      break 
    }
    if restart { restart = false; continue; }
    // parse block 
    for true {
      // for debugging
      // add "line "; lines; add " char "; chars; add ": "; print; clear; 
      //add "line "; lines; add ": "; print; clear; 
      //unstack; print; stack; add "\n"; print; clear;
      // ----------------
      // 2 tokens
      mm.pop();
      mm.pop();
      // ignore duplicated timestamps. 
      if (mm.work == "timestamp*timestamp*") {
        mm.work = ""          // clear
        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 += "timestamp*"
        mm.push();
        continue
      }
      // handle multiline comments
      if (mm.work == "comment*comment*") {
        mm.work = ""          // clear
        mm.work += mm.tape[mm.cell] /* get */
        mm.work += "\n"
        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 += "comment*"
        mm.push();
        continue
      }
      // dont need because an initial recordset always exists
      //"record*record*","recordset*record*" {
      if (mm.work == "recordset*record*") {
        mm.work = ""          // clear
        mm.work += mm.tape[mm.cell] /* get */
        mm.work += "\n"
        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
        // debug code
        // a+; count; add " record!\n"; print; clear;
        mm.work += "recordset*"
        mm.push();
        continue
      }
      // this will be compiled differently from r*r*
      if (mm.work == "recordset*command*") {
        // clear; get; add "\n"; ++; get; --; put; clear;
        // just ignore commands with no comments
        mm.counter++    /* a+ */
        // count filtered commands
        mm.work = ""          // clear
        mm.work += "recordset*"
        mm.push();
        continue
      }
      if (mm.work == "recordset*trivial*") {
        mm.counter++    /* a+ */
        // count filtered commands
        mm.work = ""          // clear
        mm.work += "recordset*"
        mm.push();
        continue
      }
      if (mm.eof) {
        // clean up trailing comments etc
        if (mm.work == "recordset*timestamp*" || mm.work == "recordset*comment*") {
          mm.work = ""          // clear
          mm.work += "recordset*record*"
          mm.push();
          mm.push();
          continue
        }
      }
      // -------------
      // 3 parse tokens
      mm.pop();
      // remove trivial commands without comments
      if (mm.work == "recordset*timestamp*trivial*") {
        mm.counter++    /* a+ */
        // count filtered commands
        mm.work = ""          // clear
        mm.work += "recordset*"
        mm.push();
        continue
      }
      // ignore duplicated timestamps. 
      if (mm.work == "timestamp*comment*timestamp*") {
        mm.work = ""          // clear
        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.increment()     /* ++ */ 
        
        mm.increment()     /* ++ */ 
        
        mm.work += mm.tape[mm.cell] /* get */
        if mm.cell > 0 { mm.cell-- }  /* -- */
        mm.tape[mm.cell] = mm.work  /* put */
        if mm.cell > 0 { mm.cell-- }  /* -- */
        mm.work = ""          // clear
        mm.work += "comment*timestamp*"
        mm.push();
        mm.push();
        continue
      }
      // amalgamate comments before and after the timestamp
      if (mm.work == "comment*timestamp*comment*") {
        mm.work = ""          // clear
        mm.work += mm.tape[mm.cell] /* get */
        mm.increment()     /* ++ */ 
        
        mm.increment()     /* ++ */ 
        
        mm.work += "\n"
        mm.work += mm.tape[mm.cell] /* get */
        if mm.cell > 0 { mm.cell-- }  /* -- */
        if mm.cell > 0 { mm.cell-- }  /* -- */
        mm.tape[mm.cell] = mm.work  /* put */
        mm.work = ""          // clear
        mm.work += "comment*timestamp*"
        mm.push();
        mm.push();
        continue
      }
      if (mm.work == "comment*timestamp*command*" || mm.work == "comment*timestamp*trivial*") {
        mm.work = ""          // clear
        mm.work += mm.tape[mm.cell] /* get */
        mm.work += "\n"
        mm.increment()     /* ++ */ 
        
        mm.work += mm.tape[mm.cell] /* get */
        mm.work += "\n"
        mm.increment()     /* ++ */ 
        
        mm.work += mm.tape[mm.cell] /* get */
        if mm.cell > 0 { mm.cell-- }  /* -- */
        if mm.cell > 0 { mm.cell-- }  /* -- */
        mm.tape[mm.cell] = mm.work  /* put */
        mm.work = ""          // clear
        mm.work += "record*"
        mm.push();
        continue
      }
      // dont remove trivial commands with comments
      if (mm.work == "timestamp*comment*command*" || mm.work == "timestamp*comment*trivial*") {
        mm.work = ""          // clear
        // switch the order to make comment precede timestamp
        mm.increment()     /* ++ */ 
        
        mm.work += mm.tape[mm.cell] /* get */
        mm.work += "\n"
        if mm.cell > 0 { mm.cell-- }  /* -- */
        mm.work += mm.tape[mm.cell] /* get */
        mm.work += "\n"
        mm.increment()     /* ++ */ 
        
        mm.increment()     /* ++ */ 
        
        mm.work += mm.tape[mm.cell] /* get */
        if mm.cell > 0 { mm.cell-- }  /* -- */
        if mm.cell > 0 { mm.cell-- }  /* -- */
        mm.tape[mm.cell] = mm.work  /* put */
        mm.work = ""          // clear
        mm.work += "record*"
        mm.push();
        continue
      }
      if (mm.work == "recordset*timestamp*command*") {
        // clear; ++; get; add "\n"; ++; get; --; put; --; clear;
        // just ignore commands with not comments
        mm.counter++    /* a+ */
        // count filtered
        mm.work += "recordset*"
        mm.push();
        continue
      }
      // resolve commands and trivial command with comments
      if (mm.work == "recordset*comment*command*" || mm.work == "recordset*comment*trivial*") {
        mm.work = ""          // clear
        mm.increment()     /* ++ */ 
        
        mm.work += mm.tape[mm.cell] /* get */
        mm.work += "\n"
        mm.increment()     /* ++ */ 
        
        mm.work += mm.tape[mm.cell] /* get */
        if mm.cell > 0 { mm.cell-- }  /* -- */
        mm.tape[mm.cell] = mm.work  /* put */
        if mm.cell > 0 { mm.cell-- }  /* -- */
        mm.work = ""          // clear
        mm.work += "recordset*record*"
        mm.push();
        mm.push();
        continue
      }
      mm.push();
      mm.push();
      mm.push();
      if (mm.eof) {
        mm.pop();
        mm.pop();
        if (mm.work != "recordset*") {
          mm.push();
          mm.push();
          mm.work += "# History file did not parse well!\n"
          fmt.Printf("%s", mm.work)    // print
          mm.work = ""          // clear
          mm.work += "# Parse stack was: "
          fmt.Printf("%s", mm.work)    // print
          mm.work = ""          // clear
          for mm.pop() {}   /* unstack */ 
          mm.work += "\n"
          fmt.Printf("%s", mm.work)    // print
          os.Exit(0)
        }
        if (mm.work == "recordset*") {
          mm.work = ""          // clear
          mm.work += mm.tape[mm.cell] /* get */
          mm.work += "\n# History file parsed and filtered by pars/eg/bash.history.pss \n"
          mm.work += "# "
          mm.work += strconv.Itoa(mm.counter) /* count */ 
          mm.work += " trivial commands (without preceding comments) were removed.\n"
          fmt.Printf("%s", mm.work)    // print
        }
      }
      break 
    } // parse
    
  }
}


// end of generated golang code