// 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 func (mm *machine) unescapeChar(c string) { // if mm.work = "" { return } mm.work = strings.Replace(mm.work, "\\"+c, c, -1) } // 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 { 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 */ // make character numbers (for error messages) relative // to line numbers if (mm.work == "\n") { mm.work = "" // clear mm.charsRead = 0 /* nochars */ break } if (isInClass(unicode.IsSpace, mm.work)) { mm.work = "" // clear break } if (mm.work == "+" || mm.work == "-") { mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "opadd*" mm.push(); break } if (mm.work == "*" || mm.work == "/") { mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "opmul*" mm.push(); break } // modern pascal style comments, but not (* ... *) because // they are tricky to parse. if (mm.work == "{") { // save the line number for possible error message later mm.work = "" // clear mm.work += strconv.Itoa(mm.linesRead) /* lines */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "{" mm.until("}"); if (strings.HasSuffix(mm.work, "}")) { // can convert to another format mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear // create a "comment" parse token // comment-out this line to remove multiline comments from the // translated code // add "comment*"; push; break } // make an unterminated multiline comment an error // to ease debugging of scripts. mm.work = "" // clear mm.work += "[pl/0 error] Unterminated pascal comment { ... } \n" mm.work += "stating at line number " mm.work += mm.tape[mm.cell] /* get */ mm.work += "\n" fmt.Printf("%s", mm.work) // print mm.work = "" // clear os.Exit(0) } if (mm.work == "*" || mm.work == "/") { mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "opmul*" mm.push(); break } // literal tokens // '!' is the print command in pl/0 // '?' is the read command in pl/0 // '#' means != // '.' marks the end of a program. if (mm.work == "." || mm.work == "," || mm.work == "=" || mm.work == ";" || mm.work == "(" || mm.work == ")") { mm.tape[mm.cell] = mm.work /* put */ mm.work += "*" mm.push(); break } if (mm.work == "!") { mm.work = "" // clear mm.work += "write" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "!*" mm.push(); break } if (mm.work == "?") { mm.work = "" // clear mm.work += "read" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "?*" mm.push(); break } // also need to parse <= >= comparison operators // '=' is comparison and also constant assignment. if (mm.work == "<" || mm.work == ">" || mm.work == "#") { /* while */ for isInList("=", string(mm.peep)) { if mm.eof { break } mm.read() } if (mm.work != "<" && mm.work != ">" && mm.work != ">=" && mm.work != "<=" && mm.work != "#") { // error, message and quit mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "[pl/0 error] unrecognised operator " mm.work += mm.tape[mm.cell] /* get */ mm.work += " at line/char " mm.work += strconv.Itoa(mm.linesRead) /* lines */ mm.work += "/" mm.work += strconv.Itoa(mm.charsRead) /* chars */ mm.work += "\n" fmt.Printf("%s", mm.work) // print os.Exit(0) } if (mm.work == "#") { mm.work = "" // clear mm.work += "!=" } mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "compare*" mm.push(); } if (mm.work == ":") { if (mm.eof) { mm.work = "" // clear mm.work += "trailing : at end of file!\n" fmt.Printf("%s", mm.work) // print os.Exit(0) } mm.read() /* read */ if (mm.work == ":=") { mm.tape[mm.cell] = mm.work /* put */ mm.work += "*" mm.push(); break } mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "[pl/0 error] unrecognised operator " mm.work += mm.tape[mm.cell] /* get */ mm.work += " at line/char " mm.work += strconv.Itoa(mm.linesRead) /* lines */ mm.work += "/" mm.work += strconv.Itoa(mm.charsRead) /* chars */ mm.work += "\n" fmt.Printf("%s", mm.work) // print os.Exit(0) } if (isInRange('0','9', mm.work)) { /* while */ for isInRange('0','9', string(mm.peep)) { if mm.eof { break } mm.read() } mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "number*" mm.push(); break } if (isInClass(unicode.IsLetter, mm.work)) { /* while */ for isInClass(unicode.IsLetter, string(mm.peep)) { if mm.eof { break } mm.read() } // make keywords case insensitive mm.tape[mm.cell] = mm.work /* put */ mm.work = strings.ToLower(mm.work) /* lower */ // synonym with ! if (mm.work == "write" || mm.work == "writeln") { mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "!*" mm.push(); break } // keywords in pl/0 if (mm.work == "const" || mm.work == "var" || mm.work == "if" || mm.work == "then" || mm.work == "while" || mm.work == "do" || mm.work == "begin" || mm.work == "end" || mm.work == "proc" || mm.work == "procedure" || mm.work == "call" || mm.work == "odd") { // or add ".key*" to remind us that these are keywords, // not parse tokens // add a space here for pretty printing later mm.tape[mm.cell] = mm.work /* put */ if (mm.work == "procedure") { mm.work = "" // clear mm.work += "proc" } mm.work += "*" mm.push(); break } // restore non-lower case identifier mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "ident*" mm.push(); break } if (mm.work != "") { // error unrecognised character mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += strconv.Itoa(mm.linesRead) /* lines */ mm.work += ":" mm.work += strconv.Itoa(mm.charsRead) /* chars */ mm.work += " [pl/0 error] incorrect character '" mm.work += mm.tape[mm.cell] /* get */ mm.work += "'\n" fmt.Printf("%s", mm.work) // print os.Exit(0) } break } if restart { restart = false; continue; } // parse block for true { // To visualise token reduction uncomment this below: mm.work += strconv.Itoa(mm.linesRead) /* lines */ mm.work += ":" mm.work += strconv.Itoa(mm.charsRead) /* chars */ mm.work += " " fmt.Printf("%s", mm.work) // print mm.work = "" // clear mm.work += "\n" for mm.pop() {} /* unstack */ fmt.Printf("%s", mm.work) // print mm.clip() for mm.push() {} /* stack */ // we will do these 5 token reductions first, so that // "statement*end*" does not have precedence mm.pop(); mm.pop(); mm.pop(); mm.pop(); mm.pop(); // sometimes statements are terminated by expressions, which // in turn must be terminated by something else, so there is a // trailing token there if (strings.HasPrefix(mm.work, "if*condition*then*statement*") && mm.work != "if*condition*then*statement*") { /* replace */ mm.work = strings.Replace(mm.work, "if*condition*then*statement*", "statement*", -1) mm.push(); mm.push(); if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work += mm.tape[mm.cell] /* get */ mm.work += " " mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.increment() /* ++ */ mm.work += " " 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-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear // transfer 'invisible' token value mm.increment() /* ++ */ mm.increment() /* ++ */ mm.increment() /* ++ */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work = "" // clear mm.increment() /* ++ */ mm.increment() /* ++ */ continue } if (strings.HasPrefix(mm.work, "while*condition*do*statement*") && mm.work != "while*condition*do*statement*") { /* replace */ mm.work = strings.Replace(mm.work, "while*condition*do*statement*", "statement*", -1) mm.push(); mm.push(); if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work += mm.tape[mm.cell] /* get */ mm.work += " " mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.increment() /* ++ */ mm.work += " " 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-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear // transfer invisible token value, and realign tape pointer mm.increment() /* ++ */ mm.increment() /* ++ */ mm.increment() /* ++ */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work = "" // clear mm.increment() /* ++ */ mm.increment() /* ++ */ continue } mm.push(); mm.push(); mm.push(); mm.push(); mm.push(); //----------------- // 1 token mm.pop(); // errors if (mm.eof && mm.work != ".*") { mm.work = "" // clear mm.work += strconv.Itoa(mm.linesRead) /* lines */ mm.work += ":" mm.work += strconv.Itoa(mm.charsRead) /* chars */ mm.work += " [pl/0 error] Missing '.' at end of program ? \n" mm.work += " \n" fmt.Printf("%s", mm.work) // print os.Exit(0) } //----------------- // 2 tokens mm.pop(); // Some errors if (mm.work == "ident*ident*") { mm.work = "" // clear mm.work += strconv.Itoa(mm.linesRead) /* lines */ mm.work += ":" mm.work += strconv.Itoa(mm.charsRead) /* chars */ mm.work += " [pl/0 error] 2 variable names in a row (is there a \n" mm.work += " missing operator?) \n" fmt.Printf("%s", mm.work) // print os.Exit(0) } if (mm.work == "vardec*const*") { mm.work = "" // clear mm.work += strconv.Itoa(mm.linesRead) /* lines */ mm.work += ":" mm.work += strconv.Itoa(mm.charsRead) /* chars */ mm.work += " [pl/0 error] Constant declarations must precede variable \n" mm.work += " declarations in pl/0 \n" fmt.Printf("%s", mm.work) // print os.Exit(0) } if (mm.work == "condec*const*") { mm.work = "" // clear mm.work += strconv.Itoa(mm.linesRead) /* lines */ mm.work += ":" mm.work += strconv.Itoa(mm.charsRead) /* chars */ mm.work += " [pl/0 error] only 1 constant declaration block is allowed \n" mm.work += " in each scope \n" fmt.Printf("%s", mm.work) // print os.Exit(0) } if (mm.work == "vardec*var*") { mm.work = "" // clear mm.work += strconv.Itoa(mm.linesRead) /* lines */ mm.work += ":" mm.work += strconv.Itoa(mm.charsRead) /* chars */ mm.work += " [pl/0 error] only 1 variable declaration block is allowed \n" mm.work += " in each scope \n" fmt.Printf("%s", mm.work) // print os.Exit(0) } if (mm.work == ";*end*") { mm.work = "" // clear mm.work += strconv.Itoa(mm.linesRead) /* lines */ mm.work += ":" mm.work += strconv.Itoa(mm.charsRead) /* chars */ mm.work += " [pl/0 error] Last statement in block does not require \n" mm.work += " a semi-colon ';' \n" fmt.Printf("%s", mm.work) // print os.Exit(0) } // add an empty procset* (set of procedures) if there is // not one already. This simplifies program reduction later // it doesnt matter if there is 1 or 2 tokens here. if (strings.HasSuffix(mm.work, "proc*") && !strings.HasPrefix(mm.work,"procset*")) { if (mm.work != "proc*") { // need to conserve the preceding token mm.push(); mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ mm.increment() /* ++ */ mm.tape[mm.cell] = mm.work /* put */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work = "" // clear mm.tape[mm.cell] = mm.work /* put */ mm.work += "procset*proc*" mm.push(); mm.push(); continue } // only 1 token mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ mm.increment() /* ++ */ mm.tape[mm.cell] = mm.work /* put */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work = "" // clear mm.tape[mm.cell] = mm.work /* put */ mm.work += "procset*proc*" mm.push(); mm.push(); continue } // procedure headers (name etc) if (mm.work == "procset*proceed*") { 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 += "procset*" mm.push(); continue } // "=" can be used for constant assignment but also as // a comparison operator if (mm.work == "=*ident*" || mm.work == "=*exp*") { /* replace */ mm.work = strings.Replace(mm.work, "=*", "compare*", -1) mm.push(); mm.push(); continue } if (mm.work == "exp*=*") { /* replace */ mm.work = strings.Replace(mm.work, "=*", "compare*", -1) mm.push(); mm.push(); continue } // expression transmog if (mm.work == "opmul*ident*" || mm.work == "opadd*ident*" || mm.work == "opmul*number*" || mm.work == "opadd*number*" || mm.work == "compare*ident*" || mm.work == "compare*number*" || mm.work == ":=*ident*" || mm.work == ":=*number*" || mm.work == "if*ident*" || mm.work == "if*number*" || mm.work == "while*ident*" || mm.work == "while*number*" || mm.work == "(*ident*" || mm.work == "(*number*" || mm.work == "!*ident*" || mm.work == "!*number*") { mm.push(); mm.work = "" // clear mm.work += "exp*" mm.push(); continue } // expression transmutation if (mm.work == "ident*opmul*" || mm.work == "ident*opadd*" || mm.work == "number*opmul*" || mm.work == "number*opadd*" || mm.work == "ident*compare*" || mm.work == "number*compare*") { /* replace */ mm.work = strings.Replace(mm.work, "ident*", "exp*", -1) /* replace */ mm.work = strings.Replace(mm.work, "number*", "exp*", -1) mm.push(); mm.push(); continue } if (mm.work == "const*conlist*") { mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ 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 += "condec*" mm.push(); continue } // non-tail reduction for variables if (mm.work == "var*ident*") { mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ 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 += "varlist*" mm.push(); continue } // variable decs if (mm.work == "varlist*;*") { 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 += "vardec*" mm.push(); continue } if (mm.work == "block*.*") { mm.work = "" // clear mm.work += "program*" mm.push(); continue } if (mm.work == "call*ident*") { mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ 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 += "statement*" mm.push(); continue } if (mm.work == "?*ident*") { 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 += "statement*" mm.push(); continue } // a multi statement block, between begin/end if (mm.work == "begin*statementset*") { // ident statements, 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-- } /* -- */ /* replace */ mm.work = strings.Replace(mm.work, "\n", "\n ", -1) mm.work += "\nend" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "statement*" mm.push(); continue } // tail reduction for statementsets if (mm.work == "statement*end*") { mm.work = "" // clear //get; ++; get; --; put; clear; mm.work += "statementset*" mm.push(); continue } //----------------- // 3 tokens mm.pop(); // ! expression must be parsed while looked at the // trailing token, like all expression parsing if (strings.HasPrefix(mm.work, "!*exp*") && mm.work != "!*exp*" && !strings.HasSuffix(mm.work,"opmul*") && !strings.HasSuffix(mm.work,"opadd*")) { // need to conserve the "invisible" last token /* replace */ mm.work = strings.Replace(mm.work, "!*exp*", "statement*", -1) mm.push(); mm.push(); // also transfer attributes if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work += mm.tape[mm.cell] /* get */ 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.increment() /* ++ */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ mm.increment() /* ++ */ continue } // procedure headers (name etc) if (mm.work == "proc*ident*;*") { mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ mm.work += " " mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ 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 += "prochead*" mm.push(); continue } // procedure headers (name etc) if (mm.work == "prochead*statement*;*") { // indent the statement if it is not a begin/end construct mm.work = "" // clear mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ if (!strings.HasPrefix(mm.work,"begin")) { mm.work = "" // clear mm.work += " " mm.work += mm.tape[mm.cell] /* get */ /* replace */ mm.work = strings.Replace(mm.work, "\n", "\n ", -1) mm.tape[mm.cell] = mm.work /* put */ } if mm.cell > 0 { mm.cell-- } /* -- */ mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ mm.work += "\n" mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ 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 += "proceed*" mm.push(); continue } // expressions, this could be the trickiest aspect of // the grammar. transmog ident/number to exp if (mm.work == "exp*opmul*exp*" || mm.work == "exp*opadd*exp*") { mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ 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 += "exp*" mm.push(); continue } if (mm.work == "(*exp*)*") { mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ 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 += "exp*" mm.push(); continue } // this is reverse reduction of statements, which can be // useful since exp* requires a trailing token in order to // resolve. if (mm.work == "statement*;*statementset*") { mm.work = "" // clear 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 += "statementset*" mm.push(); continue } // variable decs if (mm.work == "varlist*,*ident*") { mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.work += " " 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 += "varlist*" mm.push(); continue } //----------------- // 4 tokens mm.pop(); // procedure headers (name etc). Need to indent the variable decs etc if (mm.work == "prochead*vardec*statement*;*" || mm.work == "prochead*condec*statement*;*") { // indent the variable/constant declaration mm.work = "" // clear mm.work += " " mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ /* replace */ mm.work = strings.Replace(mm.work, "\n", "\n ", -1) mm.tape[mm.cell] = mm.work /* put */ if mm.cell > 0 { mm.cell-- } /* -- */ 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 */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "proceed*" mm.push(); continue } // ident and number have already been transmog'ed into exp* // and =* into compare* if (strings.HasPrefix(mm.work, "exp*compare*exp*") && mm.work != "exp*compare*exp*" && !strings.HasSuffix(mm.work,"opmul*") && !strings.HasSuffix(mm.work,"opadd*")) { // need to conserve the "invisible" last token /* replace */ mm.work = strings.Replace(mm.work, "exp*compare*exp*", "condition*", -1) mm.push(); mm.push(); if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work += mm.tape[mm.cell] /* get */ mm.work += " " mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.work += " " 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 // transfer trailing token value mm.increment() /* ++ */ 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.increment() /* ++ */ mm.work = "" // clear continue } // also see the 5 token reduction, because of the trailing token // required by exp* if (mm.work == "if*condition*then*statement*" || mm.work == "while*condition*do*statement*") { // indent the statement if it is not a begin/end construct mm.work = "" // clear mm.increment() /* ++ */ mm.increment() /* ++ */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ if (!strings.HasPrefix(mm.work,"begin")) { mm.work = "" // clear mm.work += " " mm.work += mm.tape[mm.cell] /* get */ /* replace */ mm.work = strings.Replace(mm.work, "\n", "\n ", -1) mm.tape[mm.cell] = mm.work /* put */ } if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work = "" // clear mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ mm.work += " " mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.work += " " 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-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "statement*" mm.push(); continue } // lookahead for expressions in statements // the problem is: x:=4*3+1 will be parsed at // statement*+1 if no lookahead. // If the expression // is not followed by a */+- then it is a complete statement // and can be reduced. lets transmog ident and number to make simpler if (strings.HasPrefix(mm.work, "ident*:=*exp*") && mm.work != "ident*:=*exp*" && !strings.HasSuffix(mm.work,"opmul*") && !strings.HasSuffix(mm.work,"opadd*")) { // need to conserve the "invisible" last token /* replace */ mm.work = strings.Replace(mm.work, "ident*:=*exp*", "statement*", -1) mm.push(); mm.push(); if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work += mm.tape[mm.cell] /* get */ mm.work += " " mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.work += " " 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.increment() /* ++ */ mm.increment() /* ++ */ continue } // tail reduction for constant decs if (mm.work == "ident*=*number*;*") { mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ mm.work += " " mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.work += " " mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "conlist*" mm.push(); continue } // if (mm.work == "ident*;*") { 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 += "varlist*" mm.push(); continue } //----------------- // 5 tokens mm.pop(); // procedure headers (name etc), need to indent condec vardec if (mm.work == "prochead*condec*vardec*statement*;*") { // indent the variable and constant declarations mm.work = "" // clear mm.work += " " mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ /* replace */ mm.work = strings.Replace(mm.work, "\n", "\n ", -1) mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += " " mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ /* replace */ mm.work = strings.Replace(mm.work, "\n", "\n ", -1) mm.tape[mm.cell] = mm.work /* put */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ 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 */ mm.work += "\n" mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "proceed*" mm.push(); continue } // constant declarations, tail reduction, but tail redux not // necessary if (mm.work == "ident*=*number*,*conlist*") { mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ mm.work += " " mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.work += " " mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.work += " " mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "conlist*" mm.push(); continue } // program reduction if (mm.eof) { if (mm.work == "statement*.*") { 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 += "program*" } if (mm.work == "vardec*statement*.*" || mm.work == "condec*statement*.*" || mm.work == "procset*statement*.*") { mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ mm.work += "\n{ main program }\n" mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ 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 += "program*" } if (mm.work == "condec*vardec*statement*.*" || mm.work == "vardec*procset*statement*.*" || mm.work == "condec*procset*statement*.*") { 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{ main program }\n" mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "program*" } if (mm.work == "condec*vardec*procset*statement*.*") { 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 */ mm.work += "\n{ main program }\n" mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "program*" } } if (mm.eof) { for mm.pop() {} /* unstack */ if (mm.work == "program*") { mm.work = "" // clear mm.work += "{ ok pl/0 }\n" mm.work += "{ code checked and formatted by eg/plzero.pss }\n" mm.work += mm.tape[mm.cell] /* get */ mm.work += "\n" fmt.Printf("%s", mm.work) // print os.Exit(0) } mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "Pl/0 program did not parse well \n" mm.work += "The final parse tokens were: " mm.work += mm.tape[mm.cell] /* get */ mm.work += "\n" fmt.Printf("%s", mm.work) // print os.Exit(0) } mm.push(); mm.push(); mm.push(); mm.push(); mm.push(); break } // parse } } // end of generated 'go' code