// 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 { // The script lexing phase mm.read() /* read */ // spaces are actually significant because they cannot occur in // decimal numbers, eg: '4. 03' is probably an error // make character counter relative to each line for more helpful // error messages if (isInList("\n", mm.work)) { mm.charsRead = 0 /* nochars */ } if (isInClass(unicode.IsSpace, mm.work)) { mm.work = "" // clear if (!mm.eof) { restart = true; break // restart } if (mm.eof) { break } } // literal token lexing // ; is end of an expression/equation this is needed to reduce // comparison operators. if (isInList(";", mm.work)) { mm.tape[mm.cell] = mm.work /* put */ mm.work += "*" mm.push(); break } // for functions, eg binomial if (isInList(",", mm.work)) { mm.tape[mm.cell] = mm.work /* put */ mm.work += "*" mm.push(); break } // todo add % as modulus if (isInList("<>:!=|&", mm.work)) { /* while */ for isInList("<>:!=|&", string(mm.peep)) { if mm.eof { break } mm.read() } mm.tape[mm.cell] = mm.work /* put */ // this is use for 'assignment' if (mm.work == ":=") { mm.work = "" // clear mm.work += "=" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "=*" mm.push(); break } if (mm.work == "&&") { mm.work = "" // clear mm.work += " \\& " mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "op.and*" mm.push(); break } if (mm.work == "||") { mm.work = "" // clear mm.work += " | " mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "op.and*" mm.push(); break } // for symbols like theta if (mm.work == ":") { mm.work += "*" mm.push(); break } //"&&" { clear; add " \\land "; put; clear; add "op.and*"; push; .reparse } //"||" { clear; add " \\lor "; put; clear; add "op.and*"; push; .reparse } if (mm.work == "<" || mm.work == "<=" || mm.work == ">" || mm.work == ">=" || mm.work == "==" || mm.work == "!=" || mm.work == "<>") { // synonyms if (mm.work == "==") { mm.work = "" // clear mm.work += " = " } if (mm.work == "<=") { mm.work = "" // clear mm.work += " \\leq " } if (mm.work == ">=") { mm.work = "" // clear mm.work += " \\geq " } if (mm.work == "<>" || mm.work == "!=") { mm.work = "" // clear mm.work += " \\neq " } mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "op.compare*" mm.push(); break } mm.work = "" // clear mm.work += "operators" mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ mm.increment() /* ++ */ mm.tape[mm.cell] = mm.work /* put */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work = "" // clear mm.work += "maths.help*" mm.push(); mm.work += " unknown operator '" mm.work += mm.tape[mm.cell] /* get */ mm.work += "'\n" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); break } // plus and minus if (mm.work == "+" || mm.work == "-") { mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "op.add*" mm.push(); } // multiply and divide if (mm.work == "*" || mm.work == "/") { if (mm.work == "*") { mm.work = "" // clear mm.work += " \\times " } mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "op.mul*" mm.push(); } // to the power of, but what is the lisp power operator ??? if (mm.work == "^") { mm.work = "" // clear mm.work += "^" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "op.power*" mm.push(); } // brackets for grouping if (mm.work == "(") { // it useful to have a line and char for extra // opening brackets. mm.work = "" // clear mm.work += " near line:" mm.work += strconv.Itoa(mm.linesRead) /* lines */ mm.work += " char:" mm.work += strconv.Itoa(mm.charsRead) /* chars */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "(*" mm.push(); } if (mm.work == ")") { mm.tape[mm.cell] = mm.work /* put */ mm.work += "*" mm.push(); } // for decimal number if (mm.work == ".") { mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "dot*" mm.push(); } 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 += "natnum*" mm.push(); } // What characters are variable names ? if (isInClass(unicode.IsLetter, mm.work)) { /* while */ for isInClass(unicode.IsLetter, string(mm.peep)) { if mm.eof { break } mm.read() } mm.tape[mm.cell] = mm.work /* put */ // sum is a sigma sum eg \\sum{0,x} //"AND" { clear; add " \\land "; put; clear; add "op.and*"; push; .reparse } //"OR" { clear; add " \\lor "; put; clear; add "op.or*"; push; .reparse } if (mm.work == "AND") { mm.work = "" // clear mm.work += " \\& " mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "op.and*" mm.push(); break } if (mm.work == "OR") { mm.work = "" // clear mm.work += " | " mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "op.or*" mm.push(); break } if (mm.work == "sqrt" || mm.work == "squareroot" || mm.work == "cuberoot" || mm.work == "4throot" || mm.work == "5throot" || mm.work == "nthroot" || mm.work == "tan" || mm.work == "cos" || mm.work == "cosine" || mm.work == "sin" || mm.work == "sine" || mm.work == "sec" || mm.work == "cosec" || mm.work == "cot" || mm.work == "sum" || mm.work == "binomial") { // need to take care these replacements wont interfer with // each other /* replace */ mm.work = strings.Replace(mm.work, "cosec", "csc", -1) /* replace */ mm.work = strings.Replace(mm.work, "cosine", "cos", -1) /* replace */ mm.work = strings.Replace(mm.work, "sine", "sin", -1) /* replace */ mm.work = strings.Replace(mm.work, "squareroot", "sqrt", -1) /* replace */ mm.work = strings.Replace(mm.work, "cuberoot", "sqrt[3]", -1) /* replace */ mm.work = strings.Replace(mm.work, "4throot", "sqrt[4]", -1) /* replace */ mm.work = strings.Replace(mm.work, "5throot", "sqrt[5]", -1) /* replace */ mm.work = strings.Replace(mm.work, "nthroot", "sqrt[n]", -1) /* replace */ mm.work = strings.Replace(mm.work, "sum", "sum_", -1) /* replace */ mm.work = strings.Replace(mm.work, "binomial", "binom", -1) mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "\\" mm.work += mm.tape[mm.cell] /* get */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "function*" mm.push(); break } mm.work = "" // clear mm.work += "variable*" mm.push(); } // a trick to catch bad characters. // better would be a !"text" test if (mm.work != "") { mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "characters" mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ mm.increment() /* ++ */ mm.tape[mm.cell] = mm.work /* put */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work = "" // clear mm.work += "maths.help*" mm.push(); mm.work += " strange character '" mm.work += mm.tape[mm.cell] /* get */ mm.work += "' encountered." mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); break } break } if restart { restart = false; continue; } // parse block for true { // The parse phase // watch the stack at is parses: very helpful for debugging. // Comment out when the script works.fk mm.work += "% line " mm.work += strconv.Itoa(mm.linesRead) /* lines */ mm.work += " char " mm.work += strconv.Itoa(mm.charsRead) /* chars */ mm.work += ": " fmt.Printf("%s", mm.work) // print mm.work = "" // clear for mm.pop() {} /* unstack */ fmt.Printf("%s", mm.work) // print for mm.push() {} /* stack */ mm.work += "\n" fmt.Printf("%s", mm.work) // print mm.work = "" // clear //--------------- // error analysis // we can group all error analysis here to make the script more // organised. This section helps to provide good error messages to // the user. mm.pop(); if (mm.work == "maths.error*") { // get the parse stack here as well mm.work = "" // clear mm.work += "! arithmetic expression syntax:" mm.work += " near line:" mm.work += strconv.Itoa(mm.linesRead) /* lines */ mm.work += " char:" mm.work += strconv.Itoa(mm.charsRead) /* chars */ mm.work += "\n" mm.work += mm.tape[mm.cell] /* get */ mm.work += "\n" // print; // add " The parse stack:"; // print; --; print; clear; fmt.Printf("%s", mm.work) // print // provide help from the maths.help* token if one was // put on the stack. mm.work = "" // clear mm.pop(); if (mm.work == "maths.help*") { mm.push(); continue } os.Exit(0) } // using a parse token to display help if (mm.work == "maths.help*") { // the topic or category to display help for is in the // attribute mm.work = "" // clear mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ // 'brackets' is topic, 'all' is a categories if (mm.work == "functions" || mm.work == "all") { mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ mm.work += "" mm.work += "\n functions are predefined words applied to an expression. " mm.work += "\n like 'tan' 'cos' 'sin' 'sqrt' 'cuberoot' '4throot'" mm.work += "\n eg:" mm.work += "\n sqrt (3^4+1) correct" mm.work += "\n sqrt 5 incorrect " } if (mm.work == "variables" || mm.work == "all") { mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ mm.work += "" mm.work += "\n variables are any alphabetic name that is not a function" mm.work += "\n such as x abc longVar etc" mm.work += "\n eg:" mm.work += "\n var correct" mm.work += "\n var.var incorrect " } if (mm.work == "characters" || mm.work == "all") { mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ mm.work += "" mm.work += "\n legal characters for expressions " mm.work += "\n arithmetic: ^+-*/" mm.work += "\n comparison: < <= > >= == != <>" mm.work += "\n logic: && || AND OR" mm.work += "\n grouping: ()" mm.work += "\n numbers: [-+0-9.]" mm.work += "\n equations: := ; (assignment and termination)" mm.work += "\n names: any alphabetic character (for functions and variables)" mm.work += "\n whitespace: is ignored." mm.work += "\n eg:" mm.work += "\n 3*size+2^0.5; correct" mm.work += "\n time := (sin(x) > sin(y)); correct" mm.work += "\n 4#+3 incorrect (# is not a recognised operator) " } if (mm.work == "operators.summary" || mm.work == "operators" || mm.work == "all") { mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ mm.work += "" mm.work += "\n Recognised operators are:" mm.work += "\n comparison: <= < >= > == != <>" mm.work += "\n arithmetic: + - * / ^" mm.work += "\n logic: && || AND OR" mm.work += "\n grouping: ( ) " } if (mm.work == "assignment.operator" || mm.work == "operators" || mm.work == "all") { mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ mm.work += "" mm.work += "\n The assignment operator is ':=' and the left-hand-side" mm.work += "\n must be a simple variable name. Assignments must end in a " mm.work += "\n semicolon." mm.work += "\n eg:" mm.work += "\n x := y^2+z^2; correct" } if (mm.work == "logic.operators" || mm.work == "operators" || mm.work == "all") { mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ mm.work += "" mm.work += "\n The logic operators are && || 'AND' 'OR'" mm.work += "\n AND and OR (uppercase) are synonyms for && and ||" mm.work += "\n eg:" mm.work += "\n && 2*x incorrect" } if (mm.work == "compare.operators" || mm.work == "operators" || mm.work == "all") { mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ mm.work += "" mm.work += "\n The comparison operators are <= < >= > == != <>" mm.work += "\n != means not equal to and '<>' is a synonym for that" mm.work += "\n eg:" mm.work += "\n " mm.work += "\n (x==4) AND (x^2 != y) correct (expression)" mm.work += "\n sqrt(x+y^2) == x+y*3; correct (equation)" mm.work += "\n == 2*x incorrect" } if (mm.work == "operators" || mm.work == "all") { mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ mm.work += "" mm.work += "\n Operators must be between numbers or expressions. " mm.work += "\n The exception to this are +/- signs which can precede" mm.work += "\n expressions" mm.work += "\n eg:" mm.work += "\n 4^3*(3+1) correct" mm.work += "\n -(4^2) + +3.1 correct" mm.work += "\n /4+3 incorrect " } if (mm.work == "multiply.and.divide" || mm.work == "operators" || mm.work == "all") { mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ mm.work += "" mm.work += "\n Multiplication is indicated by the asterisk '*' and division" mm.work += "\n by the forward-slash '/' character. These characters must be " mm.work += "\n placed between 2 'expressions' (any combination of operators," mm.work += "\n variables and functions). The mathematical syntax " mm.work += "\n '4x+3y' is not implemented so far - i.e implicit multiplication " mm.work += "\n (but why not?)" mm.work += "\n eg:" mm.work += "\n 4*3/(1.234 ^ 6) correct" mm.work += "\n -(3+2/) incorrect (divide with no right expression) " } if (mm.work == "power.operator" || mm.work == "all") { mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ mm.work += "" mm.work += "\n The power operator is ^ and must occur between 2 numbers " mm.work += "\n or expressions" mm.work += "\n eg:" mm.work += "\n 12.345^(1/2) correct (square root)" mm.work += "\n ^4 incorrect " } if (mm.work == "brackets" || mm.work == "all") { mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ mm.work += "" mm.work += "\n brackets () are used to group expressions and must be " mm.work += "\n balanced" mm.work += "\n eg: " mm.work += "\n ((5.1+3)*10.0)*6^2 correct " mm.work += "\n (5.1+3)*10.0)*6^2 incorrect (extra close bracket) " } if (mm.work == "dots" || mm.work == "all") { mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ mm.work += "" mm.work += "\n The dot (.) is only used in positive and negative decimal" mm.work += "\n numbers. I believe that decimals with no leading 0 are not permitted" mm.work += "\n I may change this." mm.work += "\n egs: 5.13 -0.1234 +456.78 0.05" } if (mm.work == "power" || mm.work == "all") { mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ mm.work += "" mm.work += "\n The power operator is '^' and has greater precedence" mm.work += "\n than all other operators." mm.work += "\n eg: 5/6^2+2 will parse as ((5/(6^2))+2) " } mm.work += "\n\n" fmt.Printf("%s", mm.work) // print os.Exit(0) } // ---------------- // 2 token errors mm.pop(); // look for tokens that can't start a sequence if (strings.HasPrefix(mm.work, ")*") && mm.work != ")*") { mm.work = "" // clear mm.work += "brackets" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.help*" mm.push(); mm.work += " extra close bracket ) in expression?" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } // literal token errors if (strings.HasPrefix(mm.work, "dot*") && !strings.HasSuffix(mm.work,"natnum*")) { mm.work = "" // clear mm.work += "dots" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.help*" mm.push(); mm.work += " Misplaced dot or incorrect decimal number ? " mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } if (strings.HasSuffix(mm.work, "dot*") && !strings.HasPrefix(mm.work,"natnum*")) { mm.work = "" // clear mm.work += "dots" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.help*" mm.push(); mm.work += " Misplaced dot (eg .123 is not implemented) ? " mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } // the not equals test is superfluous but here for clarity if (strings.HasPrefix(mm.work, "(*") && mm.work != "(*") { if (strings.HasSuffix(mm.work, "op.mul*") || strings.HasSuffix(mm.work, "op.power*") || strings.HasSuffix(mm.work, ")*")) { mm.work = "" // clear mm.work += "brackets" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.help*" mm.push(); mm.work += " misplaced bracket or operator? " mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } } // lits: ().=;: // expr* natnum* decnum* op.compare* op.power* op.mul* op.add* sign* // variable* function* // op.compare <= >= == != <> // needs more thought if (strings.HasPrefix(mm.work, "op.compare*") && mm.work != "op.compare") { if (strings.HasSuffix(mm.work, "op.mul*") || strings.HasSuffix(mm.work, "op.power") || strings.HasSuffix(mm.work, ")*") || strings.HasSuffix(mm.work, ".*")) { mm.work = "" // clear mm.work += "operators" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.help*" mm.push(); mm.work += " Misplaced operator ? " mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } } // look for sequences if (strings.HasPrefix(mm.work, "op.add*") && mm.work != "op.add") { if (strings.HasSuffix(mm.work, "op.mul*") || strings.HasSuffix(mm.work, "op.power") || strings.HasSuffix(mm.work, ")*")) { mm.work = "" // clear mm.work += "operators" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.help*" mm.push(); mm.work += " Misplaced operator ? " mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } } // allow ** as power? if (strings.HasPrefix(mm.work, "op.mul*") && mm.work != "op.mul*") { if (strings.HasSuffix(mm.work, "op.add") || strings.HasSuffix(mm.work, "op.power") || strings.HasSuffix(mm.work, ")*")) { mm.work = "" // clear mm.work += "operators" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.help*" mm.push(); mm.work += " Misplaced operator ? " mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } } if (strings.HasPrefix(mm.work, "op.power*") && mm.work != "op.power*") { if (strings.HasSuffix(mm.work, "op.add") || strings.HasSuffix(mm.work, "op.power*") || strings.HasSuffix(mm.work, "op.power") || strings.HasSuffix(mm.work, ")*")) { mm.work = "" // clear mm.work += "power.operator" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.help*" mm.push(); mm.work += " Misplaced operator or bracket? " mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } } //lits: (). // expr* natnum* decnum* op.compare* op.power* op.mul* op.add* sign* // variable* function* if (strings.HasPrefix(mm.work, "natnum*") && mm.work != "natnum*") { if (strings.HasSuffix(mm.work, "expr*") || strings.HasSuffix(mm.work, "natnum*") || strings.HasSuffix(mm.work, "decnum*") || strings.HasSuffix(mm.work, "(*")) { mm.work = "" // clear mm.work += " missing operator?" mm.tape[mm.cell] = mm.work /* put */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } } if (strings.HasPrefix(mm.work, "decnum*") && mm.work != "decnum*") { if (strings.HasSuffix(mm.work, "expr*") || strings.HasSuffix(mm.work, "natnum*") || strings.HasSuffix(mm.work, "decnum*") || strings.HasSuffix(mm.work, "(*")) { mm.work = "" // clear mm.work += " missing operator?" mm.tape[mm.cell] = mm.work /* put */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } } if (strings.HasPrefix(mm.work, "expr*") && mm.work != "expr*") { if (strings.HasSuffix(mm.work, "expr*") || strings.HasSuffix(mm.work, "natnum*") || strings.HasSuffix(mm.work, "decnum*") || strings.HasSuffix(mm.work, "(*")) { mm.work = "" // clear mm.work += " missing operator?" mm.tape[mm.cell] = mm.work /* put */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } } //lits: (). // expr* natnum* decnum* op.power* op.mul* op.add* sign* // variable function if (strings.HasPrefix(mm.work, "variable*") && mm.work != "variable*") { if (strings.HasSuffix(mm.work, "variable*") || strings.HasSuffix(mm.work, "function*") || strings.HasSuffix(mm.work, "expr*") || strings.HasSuffix(mm.work, "natnum*") || strings.HasSuffix(mm.work, "decnum*") || strings.HasSuffix(mm.work, "(*") || strings.HasSuffix(mm.work, ".*")) { mm.work = "" // clear mm.work += "variables" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.help*" mm.push(); mm.work = "" // clear mm.work += " misplaced variable name?" mm.tape[mm.cell] = mm.work /* put */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } } if (strings.HasPrefix(mm.work, "function*") && mm.work != "function*" && !strings.HasSuffix(mm.work,"(*")) { mm.work = "" // clear mm.work += "functions" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.help*" mm.push(); mm.work = "" // clear mm.work += " incorrect function syntax?" mm.tape[mm.cell] = mm.work /* put */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } if (mm.eof) { // try to diagnose missing close bracket errors at end of script // eg (*expr* // we have a line/char number in the open bracket tape cell if (strings.HasPrefix(mm.work, "(*")) { mm.work = "" // clear mm.work += "* missing close bracket ) ?\n" mm.work += " At " mm.work += mm.tape[mm.cell] /* get */ mm.work += " there is an opening bracket which does \n" mm.work += " not seem to be matched with a closing bracket " mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } if (strings.HasSuffix(mm.work, "dot*")) { // provide dot help with a help parse token mm.work = "" // clear mm.work += "dot" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.help*" mm.push(); mm.work = "" // clear mm.work += " Misplaced dot '.' at end of expression. \n" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } if (strings.HasSuffix(mm.work, "op.add*") || strings.HasSuffix(mm.work, "op.mul*") || strings.HasSuffix(mm.work, "op.power*")) { mm.work = "" // clear mm.work += "operators" mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ mm.increment() /* ++ */ mm.tape[mm.cell] = mm.work /* put */ if mm.cell > 0 { mm.cell-- } /* -- */ // preserve operator in tape.cell+1 mm.work = "" // clear mm.work += "maths.help*" mm.push(); mm.work += " Misplaced operator at '" mm.work += mm.tape[mm.cell] /* get */ mm.work += "' end of expression." mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } } // ---------------- // 3 token errors mm.pop(); // todo expr op.compare followed by ; or ) are errors // unterminated comparison mm.push(); mm.push(); mm.push(); // end of error analysis // --------------- mm.pop(); // resolve numbers to expressions to simplify grammar rules if (mm.work == "decnum*" || mm.work == "variable*") { mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "expr*" mm.push(); continue } // reduce natnum to expression at end of file, no!!! if (mm.eof) { if (mm.work == "natnum*") { // check not preceded by dot (could be decimal) mm.pop(); if (mm.work != "dot*natnum*") { /* replace */ mm.work = strings.Replace(mm.work, "natnum*", "expr*", -1) mm.push(); mm.push(); continue } mm.push(); } } //----------------- // 2 token reductions mm.pop(); // eg: (:pi + 2^:pi)/2 if (mm.work == ":*expr*") { mm.work = "" // clear mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ if mm.cell > 0 { mm.cell-- } /* -- */ // mathemtical symbols and greek letters. where there is no capital // the roman letter works. eg B Beta if (mm.work == "pi" || mm.work == "Pi" || mm.work == "alpha" || mm.work == "beta" || mm.work == "gamma" || mm.work == "Gamma" || mm.work == "delta" || mm.work == "Delta" || mm.work == "epsilon" || mm.work == "eepsilon" || mm.work == "zeta" || mm.work == "eta" || mm.work == "theta" || mm.work == "Theta" || mm.work == "iota" || mm.work == "kappa" || mm.work == "lambda" || mm.work == "Lambda" || mm.work == "mu" || mm.work == "upsilon" || mm.work == "Upsilon" || mm.work == "chi" || mm.work == "psi" || mm.work == "Psi" || mm.work == "omega" || mm.work == "Omega" || mm.work == "nu" || mm.work == "xi" || mm.work == "Xi" || mm.work == "ro" || mm.work == "rro" || mm.work == "sigma" || mm.work == "Sigma" || mm.work == "tau" || mm.work == "theta" || mm.work == "infinity") { /* replace */ mm.work = strings.Replace(mm.work, "ro", "varro", -1) // a curly ro /* replace */ mm.work = strings.Replace(mm.work, "eepsilon", "varepsilon", -1) // a curly e epsilon /* replace */ mm.work = strings.Replace(mm.work, "infinity", "infty", -1) mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += " \\" mm.work += mm.tape[mm.cell] /* get */ mm.work += " " mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "variable*" mm.push(); continue } // a bit of a hack to include +/- as an addition subtraction // operator. if (mm.work == "plusminus") { mm.work = "" // clear mm.work += " \\pm " mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "op.add*" mm.push(); continue } // save the incorrect symbol name for the error // message. mm.increment() /* ++ */ mm.tape[mm.cell] = mm.work /* put */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work = "" // clear mm.work += "symbols" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.help*" mm.push(); mm.work = "" // clear mm.work += " unknown symbol '" mm.work += mm.tape[mm.cell] /* get */ mm.work += "'" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } // reduce natnums to expressions if not followed by a dot // this creatly reduces complexity of parsing rules later // already ensured not "natnum*natnum*" in error checks above. if (strings.HasPrefix(mm.work, "natnum*") && mm.work != "natnum*" && !strings.HasSuffix(mm.work,"dot*")) { /* replace */ mm.work = strings.Replace(mm.work, "natnum*", "expr*", -1) mm.push(); mm.push(); continue } if (mm.work == "sign*expr*") { mm.work = "" // clear mm.work += " " mm.work += mm.tape[mm.cell] /* get */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work += " " mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "expr*" mm.push(); continue } // a list of equations if (mm.work == "equation*equation*" || mm.work == "equationset*equation*") { 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 += "equationset*" mm.push(); continue } // There may be an issue with reducing sign*natnum in other // cases if (mm.eof) { if (mm.work == "sign*natnum*") { mm.work = "" // clear mm.work += " " mm.work += mm.tape[mm.cell] /* get */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work += " " mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "expr*" mm.push(); continue } } //----------------- // 3 tokens mm.pop(); // an equation is just an expression followed by ';' // but we need 'lookbehind' here, otherwise partial expressions // will resolve to equations leaving the prefix orphaned so to speak. // only 2 tokens, first equation // discard the semicolon if (mm.work == "expr*;*") { mm.work = "" // clear mm.work += "\\[\n" mm.work += mm.tape[mm.cell] /* get */ mm.work += "\n" mm.work += "\\]" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "equation*" mm.push(); continue } if (strings.HasSuffix(mm.work, "expr*;*")) { if (strings.HasPrefix(mm.work, "equation*") || strings.HasPrefix(mm.work, "equationset*")) { /* replace */ mm.work = strings.Replace(mm.work, "expr*;*", "equation*", -1) mm.push(); mm.push(); mm.work += "\\[\n" if mm.cell > 0 { mm.cell-- } /* -- */ mm.work += mm.tape[mm.cell] /* get */ mm.work += "\n" mm.work += "\\]" mm.tape[mm.cell] = mm.work /* put */ // realign tape pointer mm.work = "" // clear mm.increment() /* ++ */ continue } } // These are signs at the beginning of the expression // only 2 tokens if (mm.work == "op.add*natnum*" || mm.work == "op.add*expr*") { /* replace */ mm.work = strings.Replace(mm.work, "op.add*", "sign*", -1) mm.push(); mm.push(); continue } // +/- at beginning, 3 tokens if (strings.HasPrefix(mm.work, "sign*expr*") && mm.work != "sign*expr*") { /* replace */ mm.work = strings.Replace(mm.work, "sign*expr*", "expr*", -1) mm.push(); mm.push(); // need to compose attributes and transfer other attributes // compose sign/exp attribute in Lisp syntax if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work += " " mm.work += mm.tape[mm.cell] /* get */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.work += " " if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.increment() /* ++ */ mm.increment() /* ++ */ // transfer invisible token attribute 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.work = "" // clear continue } // convert +/- oppadd* token to a sign* token where appropriate // If only 2 tokens (not 3) then start of document if (strings.HasSuffix(mm.work, "op.add*expr*") && !strings.HasPrefix(mm.work,"expr*")) { /* replace */ mm.work = strings.Replace(mm.work, "op.add*expr*", "sign*expr*", -1) mm.push(); mm.push(); mm.push(); continue } if (strings.HasSuffix(mm.work, "op.add*natnum*") && !strings.HasPrefix(mm.work,"expr*")) { /* replace */ mm.work = strings.Replace(mm.work, "op.add*natnum*", "sign*natnum*", -1) mm.push(); mm.push(); mm.push(); continue } // we dont need any look ahead here because '^' the power operator // has precedence over multiply/divide/add/subtract. // precedence. But natnum* only becomes and expr* when it sees // the following token. So need another 4 token rule if (mm.work == "expr*op.power*expr*") { mm.work = "" // clear // can also remove brackets here. see op.mul divide for example mm.work += " " mm.work += mm.tape[mm.cell] /* get */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.increment() /* ++ */ mm.work += "{" mm.work += mm.tape[mm.cell] /* get */ mm.work += "}" mm.work += " " if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "expr*" mm.push(); continue } // parse positive decimal numbers like 02.345 if (mm.work == "natnum*dot*natnum*") { 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-- } /* -- */ // or parse to decnum* here to make negation better mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "expr*" mm.push(); continue } // an interesting trick to parse a 4 token pattern before // a 3 token pattern. Be aware about popping nothing (empty stack) if (mm.work == "(*expr*)*") { mm.pop(); // eg: sqrt(4+9) if (mm.work == "function*(*expr*)*") { mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ mm.work += "{" mm.increment() /* ++ */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.work += "}" if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "expr*" mm.push(); continue } // only push if something was popped if (mm.work != "(*expr*)*") { mm.push(); } mm.work = "" // clear mm.work += "(" mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.work += ")" if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "expr*" mm.push(); continue } if (mm.eof) { // natnums have already been "reduced" to expr* at end of stream if (mm.work == "expr*op.add*expr*") { mm.work = "" // clear mm.work += " " 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 */ mm.work += " " if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "expr*" mm.push(); continue } // make / into fractions because they look good. if (mm.work == "expr*op.mul*expr*") { mm.work = "" // clear mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ if mm.cell > 0 { mm.cell-- } /* -- */ if (mm.work == "/") { // remove brackets from fractions mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ if (strings.HasPrefix(mm.work, "(") && strings.HasSuffix(mm.work, ")")) { mm.clip() mm.clop() mm.tape[mm.cell] = mm.work /* put */ } mm.work = "" // clear mm.increment() /* ++ */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ if (strings.HasPrefix(mm.work, "(") && strings.HasSuffix(mm.work, ")")) { mm.clip() mm.clop() mm.tape[mm.cell] = mm.work /* put */ } if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work = "" // clear mm.work += "\\frac{" mm.work += mm.tape[mm.cell] /* get */ mm.work += "}" mm.increment() /* ++ */ mm.increment() /* ++ */ mm.work += "{" mm.work += mm.tape[mm.cell] /* get */ mm.work += "}" } if (mm.work == " \\times ") { mm.work = "" // clear mm.work += " " 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 */ mm.work += " " } if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "expr*" mm.push(); continue } } //----------------- // 4 tokens parse token reductions mm.pop(); // a better lookahead, this will work within another language // it only looks for operators with more precedence. // reduce comparisons and logic concats to expression, // so we only reduce comparisons // when surrounded by brackets or terminated with ; or , // if this works we can remove several rules below // eg: (x^2 != y) if (strings.HasPrefix(mm.work, "expr*op.compare*expr*") || strings.HasPrefix(mm.work, "expr*op.and*expr*") || strings.HasPrefix(mm.work, "expr*op.or*expr*")) { if (mm.work != "expr*op.compare*expr*" && mm.work != "expr*op.and*expr*" && mm.work != "expr*op.or*expr*") { if (strings.HasSuffix(mm.work, ")*") || strings.HasSuffix(mm.work, ",*") || strings.HasSuffix(mm.work, ";*")) { /* replace */ mm.work = strings.Replace(mm.work, "expr*op.and*expr*", "expr*", -1) /* replace */ mm.work = strings.Replace(mm.work, "expr*op.or*expr*", "expr*", -1) /* replace */ mm.work = strings.Replace(mm.work, "expr*op.compare*expr*", "expr*", -1) mm.push(); mm.push(); // assemble attribs for new exp token, if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work += " " 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 */ mm.work += " " if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ // transfer unknown token attrib mm.work = "" // clear 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.work = "" // clear // realign tape pointer mm.increment() /* ++ */ continue } } } // for latex eg in sigma sums // eg: ; if (strings.HasPrefix(mm.work, "expr*=*expr*") && mm.work != "expr*=*expr*") { if (strings.HasSuffix(mm.work, ")*") || strings.HasSuffix(mm.work, ",*") || strings.HasSuffix(mm.work, ";*")) { // check that it is a simple variable name. Slightly tricky because // variables are instantly reduced to expressions. //clear; mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ if (!isInClass(unicode.IsLetter, mm.work)) { mm.work = "" // clear mm.work += "assignment.operator" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.help*" mm.push(); mm.work += " Left-hand-side of equation not a variable? " mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work /* swap */ /* replace */ mm.work = strings.Replace(mm.work, "expr*=*expr*", "expr*", -1) mm.push(); mm.push(); // assemble attribs for new exp token, if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work += " " 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 */ mm.work += " " if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ // transfer unknown token attrib mm.work = "" // clear 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.work = "" // clear // realign tape pointer mm.increment() /* ++ */ continue } } // op.mul (*/) and op.power (^) have more precedence that +/- if (strings.HasPrefix(mm.work, "expr*op.add*expr*") && mm.work != "expr*op.add*expr*") { if (!strings.HasSuffix(mm.work,"op.mul*") && !strings.HasSuffix(mm.work,"op.power*")) { /* replace */ mm.work = strings.Replace(mm.work, "expr*op.add*expr*", "expr*", -1) mm.push(); mm.push(); // assemble attribs for new exp token, if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work += " " 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 */ mm.work += " " if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.tape[mm.cell] = mm.work /* put */ // transfer unknown token attrib mm.work = "" // clear 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.work = "" // clear // realign tape pointer mm.increment() /* ++ */ continue } } // op.power (^) has more precedence that */ if (strings.HasPrefix(mm.work, "expr*op.mul*expr*") && mm.work != "expr*op.mul*expr*") { if (!strings.HasSuffix(mm.work,"op.power*")) { /* replace */ mm.work = strings.Replace(mm.work, "expr*op.mul*expr*", "expr*", -1) mm.push(); mm.push(); // assemble times or fraction mm.work = "" // clear if mm.cell > 0 { mm.cell-- } /* -- */ mm.work += mm.tape[mm.cell] /* get */ if mm.cell > 0 { mm.cell-- } /* -- */ if (mm.work == "/") { // remove brackets from fractions mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ if (strings.HasPrefix(mm.work, "(") && strings.HasSuffix(mm.work, ")")) { mm.clip() mm.clop() mm.tape[mm.cell] = mm.work /* put */ } mm.work = "" // clear mm.increment() /* ++ */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ if (strings.HasPrefix(mm.work, "(") && strings.HasSuffix(mm.work, ")")) { mm.clip() mm.clop() mm.tape[mm.cell] = mm.work /* put */ } if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work = "" // clear mm.work += "\\frac{" mm.work += mm.tape[mm.cell] /* get */ mm.work += "}" mm.increment() /* ++ */ mm.increment() /* ++ */ mm.work += "{" mm.work += mm.tape[mm.cell] /* get */ mm.work += "}" } if (mm.work == " \\times ") { mm.work = "" // clear mm.work += " " 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 */ mm.work += " " } 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() /* ++ */ 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 // realign tape pointer mm.increment() /* ++ */ continue } } // natnum/var/decnum only become expressions when the following // token is seen, this is why we need a 'lookahead' rule here. if (strings.HasPrefix(mm.work, "expr*op.power*expr*") && mm.work != "expr*op.power*expr*") { /* replace */ mm.work = strings.Replace(mm.work, "expr*op.power*expr*", "expr*", -1) mm.push(); mm.push(); if mm.cell > 0 { mm.cell-- } /* -- */ if mm.cell > 0 { mm.cell-- } /* -- */ mm.work += " " 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 */ mm.work += " " 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() /* ++ */ 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 // realign tape pointer mm.increment() /* ++ */ continue } //----------------- // 5 tokens parse token reductions mm.pop(); //----------------- // 6 tokens parse token reductions mm.pop(); // todo: this should be 3 arguments eg sum(0,:infinity,x+y) // eg: sum(x=0,100) // need to make assignments reduce here if (mm.work == "function*(*expr*,*expr*)*") { mm.work = "" // clear mm.work += mm.tape[mm.cell] /* get */ if (mm.work != "\\sum_" && mm.work != "\\binom") { mm.work = "" // clear mm.work += "functions" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.help*" mm.push(); mm.work = "" // clear mm.work += " sum and binom take 2 arguments." mm.tape[mm.cell] = mm.work /* put */ mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "maths.error*" mm.push(); continue } mm.increment() /* ++ */ mm.increment() /* ++ */ mm.work += "{" mm.work += mm.tape[mm.cell] /* get */ mm.work += "}" // sum has a funny syntax if (strings.HasPrefix(mm.work, "\\sum")) { mm.work += "^" } mm.work += "{" mm.increment() /* ++ */ mm.increment() /* ++ */ mm.work += mm.tape[mm.cell] /* get */ mm.work += "}" 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 += "expr*" mm.push(); continue } mm.push(); mm.push(); mm.push(); mm.push(); mm.push(); mm.push(); if (mm.eof) { mm.pop(); if (mm.work == "expr*") { mm.work = "" // clear mm.work += "\\[\n" mm.work += mm.tape[mm.cell] /* get */ mm.work += "\n" mm.work += "\\]" mm.tape[mm.cell] = mm.work /* put */ mm.work = "" // clear mm.work += "equation*" mm.push(); continue } mm.push(); } if (mm.eof) { mm.pop(); mm.pop(); if (mm.work == "expr*" || mm.work == "equation*" || mm.work == "equationset*") { mm.work = "" // clear // create a latex document to display the equation mm.work += "\\documentclass{article}\n" mm.work += "\\usepackage{amsmath}\n" mm.work += "% or \\usepackage{mathtools}\n" mm.work += "\\usepackage{xcolor}\n" mm.work += "\\usepackage{multicol}\n" mm.work += "\\begin{document}\n" // turn off page numbers mm.work += "\\pagestyle{empty}\n" mm.work += "\\huge\n" mm.work += "\\color{blue}\n\n" mm.work += mm.tape[mm.cell] /* get */ mm.work += "\n" mm.work += "\\end{document}\n" fmt.Printf("%s", mm.work) // print mm.work = "" // clear os.Exit(0) } mm.push(); mm.push(); mm.work += "No, it doesn't look like a single valid arithmetic \n" mm.work += "'in-fix' expression. The 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) } break } // parse } } // end of generated 'go' code