// 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, ">", ">", -1) /* replace */ mm.work = strings.Replace(mm.work, "<", "<", -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, "'", "’", -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’t", -1) /* replace */ mm.work = strings.Replace(mm.work, "Im", "I’m", -1) /* replace */ mm.work = strings.Replace(mm.work, "cant", "can’t", -1) /* replace */ mm.work = strings.Replace(mm.work, "arent", "aren’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 += ">>" 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, ">", ">", -1) /* replace */ mm.work = strings.Replace(mm.work, "<", "<", -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, ">", ">", -1) /* replace */ mm.work = strings.Replace(mm.work, "<", "<", -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, ">", ">", -1) /* replace */ mm.work = strings.Replace(mm.work, "<", "<", -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, ">", ">", -1) /* replace */ mm.work = strings.Replace(mm.work, "<", "<", -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, ">", ">", -1) /* replace */ mm.work = strings.Replace(mm.work, "<", "<", -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 >) if (strings.HasPrefix(mm.work, ">---")) { // 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, ">", ">", -1) /* replace */ mm.work = strings.Replace(mm.work, "<", "<", -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 == ">>") { 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 ">" ">"; replace "<" "<"; /* 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, ">", ">", -1) /* replace */ mm.work = strings.Replace(mm.work, "<", "<", -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, ">", ">", -1) /* replace */ mm.work = strings.Replace(mm.work, "<", "<", -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, ">", ">", -1) /* replace */ mm.work = strings.Replace(mm.work, "<", "<", -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, ">", ">", -1) /* replace */ mm.work = strings.Replace(mm.work, "<", "<", -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, ">", ">", -1) /* replace */ mm.work = strings.Replace(mm.work, "<", "<", -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, ">", ">", -1) /* replace */ mm.work = strings.Replace(mm.work, "<", "<", -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 += "“" mm.work += mm.tape[mm.cell] /* get */ mm.work += "”" // remove the space just before the last quote (which is added // during "space*" reductions. /* replace */ mm.work = strings.Replace(mm.work, " ”", "”", -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