// -*- Mode:Common-Lisp;Package:mma; Base:10 -*- // // (c) Copyright 1990, 1992, 1994 Richard J. Fateman, Derek T. Lai // FILE -- pf.lisp // // pf.lisp defines BuildFormat to convert the parser output // or the output of the manipulative routines into // lists which resemble the PrintForm output of Mathematica. // Differences between the two reflect the fact that some // apparently useless extra levels of list are used by // mathematica, and some extra data of uncertain provenance // is included in their first arguments to HorizontalForm // and VerticalForm (the 6th entry). // This is then converted by the disp program to 2-D display. // // For example, if we type (x+y)^2 as input, the parser will return // // (Power (Plus x y ) 2) // (BuildFormat '(Power (Plus x y) 2)) returns // // (HorizontalForm 8 2 0 0 Power 190 right // ((HorizontalForm 7 1 0 0 Sequence 1000 none // ("(" (HorizontalForm 5 1 0 0 Plus 140 none (x " + " y)) ")")) // (VerticalForm 1 1 0 -1 Superscript 260 none 2))) // The version 2.0 mathematica provides for the same input.. // HorizontalForm[{8, 2, 0, 0, 0, {7, 1}}, {Power, 190, Right}, // HorizontalForm[{7, 1, 0, 0, 0, {7, 7}}, {Sequence, 1000, None}, (, // HorizontalForm[{5, 1, 0, 0, 0, {10, 2}}, {Plus, 140, None}, x, + , y], // )], , VerticalForm[{1, 1, 0, -1, 0}, {Sequence, 1000, None}, 2]] // patch history // 6/3/92 RJF fixed display of operators .. f[x][y] // (provide 'pf) // LTD: Function LOAD not yet implemented. load("mma"); "(in-package mma)"; // exported functions: define module mma export makehform, makevform, formatwidth, formatheight, atomwidth, buildformat; end module mma; // "formatstruct" is the structure representing the HorizontalForm // and the VerticalForm "PrintForm" lists. // // The meaning of its attributes are as follows: // width -- width of box // height -- height of box // hoffset -- horizontal offset // voffset -- vertical offset, distance from the base of box // op -- name of operator represented by this box // prec -- precedence of the operator // asso -- associativity of the operator // ls -- can be another HorizontalForm, VerticalForm, // or some atoms or strings. // // In the context below, "PrintForm" lists, "format", "form" are basically // referring to the same thing. define class () slot formatstruct-form = #f, init-keyword: #"formatstruct-form"; slot formatstruct-width :: = 0, init-keyword: #"formatstruct-width"; slot formatstruct-height :: = 1, init-keyword: #"formatstruct-height"; slot formatstruct-hoffset :: = 0, init-keyword: #"formatstruct-hoffset"; slot formatstruct-voffset :: = 0, init-keyword: #"formatstruct-voffset"; slot formatstruct-op = #f, init-keyword: #"formatstruct-op"; slot formatstruct-prec :: = 0, init-keyword: #"formatstruct-prec"; slot formatstruct-asso = #"none", init-keyword: #"formatstruct-asso"; slot formatstruct-ls = #f, init-keyword: #"formatstruct-ls"; end class ; // MakeVForm & MakeHForm: // functions that create horizontal and vertical forms respectively. define method makevform (w, h, hof, vof, op, prc, as, l) make-formatstruct(form: #"verticalform", width: w, height: h, hoffset: hof, voffset: vof, op: op, prec: prc, asso: as, ls: l); end method makevform; define method makehform (w, h, hof, vof, op, prc, as, l) make-formatstruct(form: #"horizontalform", width: w, height: h, hoffset: hof, voffset: vof, op: op, prec: prc, asso: as, ls: l); end method makehform; // HFormp & VFormp: // boolean functions that test whether a given form is Horizontal // or Vertical. define method hformp (form) if (not(instance?(form, ))) #f; else head(form) = #"horizontalform"; end if; end method hformp; define method vformp (form) if (not(instance?(form, ))) #f; else head(form) = #"verticalform"; end if; end method vformp; // DivideFormp, RepStrFormp, SeqFormp: // These functions tests some commonly encountered forms. // They check whether a given form is a Divide form, a // RepeatedString Form or a Sequence Form respectively. define method divideformp (f) if (not(instance?(f, ))) #f; else f.formatstruct-op = #"divide"; end if; end method divideformp; define method repstrformp (f) if (not(instance?(f, ))) #f; else head(f) = #"repeatedstring"; end if; end method repstrformp; define method seqformp (f) if (not(instance?(f, ))) #f; else f.formatstruct-op = #"sequence"; end if; end method seqformp; // tp: procedure for repeatedly testing the parser define method tp () format-out("%=", p()); write-element(*standard-output*, '\n'); tp(); end method tp; // printform: // procedure that repeatedly (by giving prompts) takes // Mathematica-acceptable (ordinary) mathematical expressions, // pass them to the parser whose output is further passwd to // BuildFormat to form the // "PrintForm" lists. define method printform () format-out("%=", buildformat(p())); // repeat, ad naseum write-element(*standard-output*, '\n'); write-element(*standard-output*, '\n'); printform(); end method printform; // formatwidth & formatheight: // gets the width and height of a format respectively. define method formatwidth (x) if (not(instance?(x, ))) atomwidth(x); else x.formatstruct-width; end if; end method formatwidth; define method formatheight (x) if (not(instance?(x, ))) 1; else x.formatstruct-height; end if; end method formatheight; // Set the precedences of the operators. This list is arranged in order of // increasing precedence. symbol-get-property(#"set", #"stringprec") := 120; symbol-get-property(#"rule", #"stringprec") := 130; symbol-get-property(#"plus", #"stringprec") := 140; symbol-get-property(#"times", #"stringprec") := 150; symbol-get-property(#"divide", #"stringprec") := 160; symbol-get-property(#"minus", #"stringprec") := 170; symbol-get-property(#"power", #"stringprec") := 190; symbol-get-property(#"dot", #"stringprec") := 210; symbol-get-property(#"sequence", #"stringprec") := 1000; // Set the correct formatter (function name to be dispatched) corresponding // to the operators symbol-get-property(#"rule", #"formatter") := #"ruleformatter"; symbol-get-property(#"plus", #"formatter") := #"plusformatter"; symbol-get-property(#"times", #"formatter") := #"timesformatter"; symbol-get-property(#"power", #"formatter") := #"powerformatter"; symbol-get-property(#"list", #"formatter") := #"listformatter"; symbol-get-property(#"dot", #"formatter") := #"dotformatter"; symbol-get-property(#"real", #"formatter") := #"realformatter"; symbol-get-property(#"set", #"formatter") := #"setformatter"; // sample definition of new formatter... 9/30/94 symbol-get-property(#"increment", #"formatter") := #"postformatter"; symbol-get-property(#"increment", #"stringprec") := 150; // guess 9/30/94 RJF symbol-get-property(#"increment", #"stringform") := "++ "; // probably could make use of more vertical/horizontal concatenators // have to keep track of precedences, though. symbol-get-property(#"factorial", #"formatter") := #"postformatter"; symbol-get-property(#"factorial", #"stringprec") := 150; // guess 9/30/94 RJF symbol-get-property(#"factorial", #"stringform") := "!"; symbol-get-property(#"factorial2", #"formatter") := #"postformatter"; symbol-get-property(#"factorial2", #"stringprec") := 150; // guess 9/30/94 RJF symbol-get-property(#"factorial2", #"stringform") := "!!"; symbol-get-property(#"preincrement", #"formatter") := #"preformatter"; symbol-get-property(#"preincrement", #"stringprec") := 150; // guess 9/30/94 RJF symbol-get-property(#"preincrement", #"stringform") := "++"; // rjf 9/30/94 define method postformatter (x) let op = head(x); let st = #f; let args = tail(x); let width = 0; let maxtoph = 1; let maxbotmh = 0; let hoffset = 0; let voffset = 0; let ls = #f; let heightpacket = #f; let ret_format = #f; ret_format := buildformat(head(args)); st := symbol-get-property(op, #"stringform"); if (not(instance?(ret_format, ))) inc!(width, atomwidth(ret_format)); ls := list(ret_format); else heightpacket := topboths(ret_format, maxtoph, maxbotmh); maxtoph := head(heightpacket); maxbotmh := tail(heightpacket); inc!(width, ret_format.formatstruct-width); voffset := max(voffset, ret_format.formatstruct-voffset); inc!(hoffset, ret_format.formatstruct-hoffset); end if; makehform(width + atomwidth(st), maxtoph + maxbotmh, hoffset, voffset, op, prec(op), #"left", list(ret_format, st)); end method postformatter; define method preformatter (x) let op = head(x); let args = tail(x); let st = #f; let width = 0; let maxtoph = 1; let maxbotmh = 0; let hoffset = 0; let voffset = 0; let ls = #f; let heightpacket = #f; let ret_format = #f; ret_format := buildformat(head(args)); st := symbol-get-property(op, #"stringform"); if (not(instance?(ret_format, ))) inc!(width, atomwidth(ret_format)); ls := list(ret_format); else heightpacket := topboths(ret_format, maxtoph, maxbotmh); maxtoph := head(heightpacket); maxbotmh := tail(heightpacket); inc!(width, ret_format.formatstruct-width); voffset := max(voffset, ret_format.formatstruct-voffset); inc!(hoffset, ret_format.formatstruct-hoffset); end if; makehform(width + atomwidth(st), maxtoph + maxbotmh, hoffset, voffset, op, prec(op), #"left", list(symbol-get-property(op, #"stringform"), ret_format)); end method preformatter; // Get precedence: // get the precedence of a given operator. Default value is 260. define method prec (x) if (instance?(x, )) 260; else let p = symbol-get-property(x, #"stringprec"); if (p) p; else 260; end if; end if; end method prec; // lessprec: // checks if the operator of a given form "f" has a lower // precedence than a given operator "op" define method lessprec (f, op) ~ not(instance?(f, )) & prec(f.formatstruct-op) < prec(op); end method lessprec; // atomwidth: // returns the width of an atom, a number or a string // (but not a formatstruct). // // much faster this way (RJF) nil(#f, nil(), nil(#f, nil(nil(nil(#f, #f))), nil(nil(nil(#f, nil(#f)))), nil(nil(nil(#f), nil(nil(nil(#f))), nil(#f))), nil(nil(nil(#f, nil(#f, "~a", #f)))))); // this is assuming IEEE arithmetic. Also a decent logarithm. // Funny about that.. define method integer-length-base-10 (x) select (x by instance?) integer(0, 9) => 1; // most common cases integer(10, 99) => 2; integer(100, 999) => 3; integer(0, 1000000) => floor(log(x, 10.0)) + 1; integer(0, 1000000000000000) => floor(log(x, 10.0D0)) + 1; integer => // rest of the cases begin let fg = floor/(ash(// LTD: Function INTEGER-LENGTH not yet implemented. integer-length(x), 19), // mult by 2^19 1741647); let try = 10 ^ fg; if (try <= x) fg + 1; else fg; end if; end; end select; end method integer-length-base-10; // we could mess with this // (defvar log10b2 (rationalize(log 10.0d0 2.0d0))) // (defvar bigint (truncate most-positive-long-float)) // // // TopBotHs: // Compare max numerator and denominator heights and // returns a height-packet of cons. nil(#f, nil(#f, #f), nil((nil(nil(nil(#f), nil(#f))))(nil(nil(#f))), nil(nil(#f, #f), nil(#f, #f)), nil(nil(#f, #f), nil(#f, #f)), nil(#f, #f))); // BuildFormat: // This function takes the output lisp expression from the parser, // look at its outermost operator (if any) and pass the whole list // to the appropriate formatter function. define method buildformat (x) let type = head(x); let formatfun = #f; // dispatch on the Head of the form if it is an atom if (not(instance?(type, )) & (formatfun := symbol-get-property(type, #"formatter"))) formatfun(x); // if it is an atom but no special formatter program defined // then use the default atom-head formatter. elseif (not(instance?(type, ))) deflt-buildformat(x); // some types are strange like (Derivative 1) or (Inverse F) // use another default formatter else deflt-buildformat-op(x); end if; end method buildformat; // set up number formatters define method numberformatter (x) if (negative?(x)) minusformatter(abs(x)); else x; end if; end method numberformatter; begin do(method (x) symbol-get-property(x, #"formatter") := numberformatter; end method, #(#"single-float", #"double-float", #"fixnum", #"bignum", #"ratio")); #(#"single-float", #"double-float", #"fixnum", #"bignum", #"ratio"); end; // not complex symbol-get-property(#"symbol", #"formatter") := method (x) if (x) x; else #"false"; end if; end method; // nil is False symbol-get-property(#"rat", #"formatter") := method (x) buildformat(outof-rat(x)); end method; symbol-get-property(#"complex", #"formatter") := #[#"lambda", #(#"x"), #(#"complexnumformatter", #"x")]; symbol-get-property(#"bigfloat", #"formatter") := method (x) buildformat(format(#f, "%=", x)); end method; // deflt-buildformat: default version... // If we do not know anything else about a given function (that is, // it is not one of plus, times, dot etc.), deflt-buildformat // handles all general functions f[x]. // // For example, it converts (f x y z) to // // (HORIZONTALFORM 10 1 0 0 F 260 LEFT (F "[" X ", " Y ", " Z "]")) define method deflt-buildformat (x) if (not(instance?(x, ))) x; else begin let op = head(x); let width = 0; let maxtoph = 1; let maxbotmh = 0; let hoffset = 0; let voffset = 0; let ls = #f; let heightpacket = #f; let ret_format = #f; for (args = cdr(x) then cdr(args), until empty?(args)) ret_format := buildformat(head(args)); if (not(instance?(ret_format, ))) inc!(width, atomwidth(ret_format) + 2); ls := apply(list, ret_format, ", ", ls); else heightpacket := topboths(ret_format, maxtoph, maxbotmh); maxtoph := head(heightpacket); maxbotmh := tail(heightpacket); inc!(width, ret_format.formatstruct-width + 2); voffset := max(voffset, ret_format.formatstruct-voffset); inc!(hoffset, ret_format.formatstruct-hoffset); ls := apply(list, ret_format, ", ", ls); end if; finally makehform(width + atomwidth(op), maxtoph + maxbotmh, hoffset, voffset, op, prec(op), #"left", apply(list, op, "[", concatenate(tail(reverse!(ls)), list("]")))); end for; end; end if; end method deflt-buildformat; define method deflt-buildformat-op (x) let op = buildformat(head(x)); let width = 0; let maxtoph = 1; let maxbotmh = 0; let hoffset = 0; let voffset = 0; let ls = #f; let heightpacket = #f; let ret_format = #f; for (args = cdr(x) then cdr(args), until // also format "op" empty?(args)) ret_format := buildformat(head(args)); if (not(instance?(ret_format, ))) inc!(width, atomwidth(ret_format) + 2); ls := apply(list, ret_format, ", ", ls); else heightpacket := topboths(ret_format, maxtoph, maxbotmh); maxtoph := head(heightpacket); maxbotmh := tail(heightpacket); inc!(width, ret_format.formatstruct-width + 2); voffset := max(voffset, ret_format.formatstruct-voffset); inc!(hoffset, ret_format.formatstruct-hoffset); ls := apply(list, ret_format, ", ", ls); end if; finally makehform(width + op.formatstruct-width, maxtoph + maxbotmh, hoffset, voffset, head(x), 260, #"left", // will 260 do?? apply(list, op, "[", concatenate(tail(reverse!(ls)), list("]")))); end for; end method deflt-buildformat-op; // listformatter: // This function handle things like {a,b,c}. It takes // (list a b c) and returns // // (HORIZONTALFORM 9 1 0 0 SEQUENCE 1000 NONE ("{" A ", " B ", " C "}")) define method listformatter (x) let width = 0; let maxtoph = 1; let maxbotmh = 0; let hoffset = 0; let voffset = 0; let ls = #f; let heightpacket = #f; let ret_format = #f; for (args = cdr(x) then cdr(args), until empty?(args)) ret_format := buildformat(head(args)); if (not(instance?(ret_format, ))) inc!(width, atomwidth(ret_format) + 2); ls := apply(list, ret_format, ", ", ls); else heightpacket := topboths(ret_format, maxtoph, maxbotmh); maxtoph := head(heightpacket); maxbotmh := tail(heightpacket); inc!(width, ret_format.formatstruct-width + 2); voffset := max(voffset, ret_format.formatstruct-voffset); inc!(hoffset, ret_format.formatstruct-hoffset); ls := apply(list, ret_format, ", ", ls); end if; finally makehform(width, maxtoph + maxbotmh, hoffset, voffset, #"sequence", prec(#"sequence"), #"none", pair("{", concatenate(tail(reverse!(ls)), list("}")))); end for; end method listformatter; // dotformatter: // This functions handles a.b.c . It takes (Dot a b c) // and returns // // (HORIZONTALFORM 9 1 0 0 DOT 210 NONE (A " . " B " . " C)) define method dotformatter (x) let width = 0; let maxtoph = 1; let maxbotmh = 0; let hoffset = 0; let voffset = 0; let ls = #f; let heightpacket = #f; let ret_format = #f; for (args = cdr(x) then cdr(args), until empty?(args)) ret_format := buildformat(head(args)); if (not(instance?(ret_format, ))) inc!(width, atomwidth(ret_format) + 3); ls := apply(list, ret_format, " . ", ls); else if (lessprec(ret_format, #"dot")) ret_format := parenformatter(ret_format); end if; heightpacket := topboths(ret_format, maxtoph, maxbotmh); maxtoph := head(heightpacket); maxbotmh := tail(heightpacket); inc!(width, ret_format.formatstruct-width + 3); voffset := max(voffset, ret_format.formatstruct-voffset); inc!(hoffset, ret_format.formatstruct-hoffset); ls := apply(list, ret_format, " . ", ls); end if; finally makehform(width - 3, maxtoph + maxbotmh, hoffset, voffset, #"dot", prec(#"dot"), #"none", tail(reverse!(ls))); end for; end method dotformatter; define method ruleformatter (x) genformatter(x, #"rule"); end method ruleformatter; symbol-get-property(#"rule", #"stringsym") := " -> "; symbol-get-property(#"rule", #"stringsymlen") := 4; define method andformatter (x) genformatter(x, #"and"); end method andformatter; symbol-get-property(#"and", #"stringsym") := " && "; symbol-get-property(#"and", #"stringsymlen") := 4; symbol-get-property(#"and", #"stringprec") := 215; symbol-get-property(#"and", #"formatter") := #"andformatter"; define method orformatter (x) genformatter(x, #"or"); end method orformatter; symbol-get-property(#"or", #"stringsym") := " || "; symbol-get-property(#"or", #"stringsymlen") := 4; symbol-get-property(#"or", #"stringprec") := 214; symbol-get-property(#"or", #"formatter") := #"orformatter"; // added 11/18/94 RJF define method alternativesformatter (x) genformatter(x, #"alternatives"); end method alternativesformatter; symbol-get-property(#"alternatives", #"stringsym") := " | "; symbol-get-property(#"alternatives", #"stringsymlen") := 3; symbol-get-property(#"alternatives", #"stringprec") := 140; // guess symbol-get-property(#"alternatives", #"formatter") := #"alternativesformatter"; // this will format most infix forms (binary, n-ary) from declarative info define method genformatter (x, head) let width = 0; let maxtoph = 1; let maxbotmh = 0; let hoffset = 0; let voffset = 0; let ls = #f; let heightpacket = #f; let sym = symbol-get-property(head, #"stringsym"); let len = symbol-get-property(head, #"stringsymlen"); let ret_format = #f; for (args = cdr(x) then cdr(args), until empty?(args)) ret_format := buildformat(head(args)); if (not(instance?(ret_format, ))) inc!(width, atomwidth(ret_format) + len); ls := apply(list, ret_format, sym, ls); else if (lessprec(ret_format, head)) ret_format := parenformatter(ret_format); end if; heightpacket := topboths(ret_format, maxtoph, maxbotmh); maxtoph := head(heightpacket); maxbotmh := tail(heightpacket); inc!(width, ret_format.formatstruct-width + len); voffset := max(voffset, ret_format.formatstruct-voffset); inc!(hoffset, ret_format.formatstruct-hoffset); ls := apply(list, ret_format, sym, ls); end if; finally makehform(width - len, maxtoph + maxbotmh, hoffset, voffset, head, prec(head), #"none", tail(reverse!(ls))); end for; end method genformatter; // plusformatter: // This function handles a+b+c . It converts (Plus a b c) // into // // (HORIZONTALFORM 9 1 0 0 PLUS 140 NONE (A " + " B " + " C)) define method plusformatter (x) let width = 0; let hoffset = 0; let voffset = 0; let ls = #f; let maxtoph = 1; let maxbotmh = 0; let heightpacket = #f; let ret_format = #f; for (args = cdr(x) then cdr(args), until empty?(args)) ret_format := buildformat(head(args)); if (not(instance?(ret_format, ))) inc!(width, atomwidth(ret_format) + 3); ls := apply(list, ret_format, " + ", ls); // beware of minuses like -x // case 1: if a plus exp begins with a minus exp, we do NOT // change the form of that minus exp elseif (ret_format.formatstruct-op = #"minus") if (args = tail(x)) inc!(width, ret_format.formatstruct-width + 3); heightpacket := topboths(ret_format, maxtoph, maxbotmh); maxtoph := head(heightpacket); maxbotmh := tail(heightpacket); ls := apply(list, ret_format, " - ", ls); else begin let ham = second(ret_format.formatstruct-ls); if (not(instance?(ham, ))) inc!(width, atomwidth(ham) + 3); // check if ham is parenthesized, handle cases like // a - b c which should not become a - (b c) else if (ham.formatstruct-op = #"sequence") let noparenham = second(ham.formatstruct-ls); if (~ lessprec(noparenham, #"plus") & // fix a-(b-c) case ~ (noparenham.formatstruct-op = #"plus")) ham := noparenham; end if; end if; inc!(width, 3 + ham.formatstruct-width); heightpacket := topboths(ham, maxtoph, maxbotmh); maxtoph := head(heightpacket); maxbotmh := tail(heightpacket); inc!(hoffset, ham.formatstruct-hoffset); voffset := max(voffset, ham.formatstruct-voffset); end if; ls := apply(list, ham, " - ", ls); end; end if; // Non-minus stuff else inc!(width, 3 + ret_format.formatstruct-width); heightpacket := topboths(ret_format, maxtoph, maxbotmh); maxtoph := head(heightpacket); maxbotmh := tail(heightpacket); inc!(hoffset, ret_format.formatstruct-hoffset); voffset := max(voffset, ret_format.formatstruct-voffset); ls := apply(list, ret_format, " + ", ls); end if; finally makehform(width - 3, // adjust spacing maxtoph + maxbotmh, hoffset, voffset, #"plus", prec(#"plus"), #"none", tail(reverse!(ls))); end for; end method plusformatter; // timesformatter: // It takes (times a b) and returns // // (HORIZONTALFORM 3 1 0 0 TIMES 150 NONE (A " " B)) define method timesformatter (x) let width = 0; let hoffset = 0; let voffset = 0; let ls = #f; let maxtoph = 1; let maxbotmh = 0; let heightpacket = #f; let ret_format = #f; let signs = 0; let x = timesreorg(x); if (isdivide(x)) divideformatter(x); // check for cases like (Times -1 Y ...), let minusformatter handle it // we'd have to change this for other kinds of negatives, e.g. bigfloats. elseif (instance?(second(x), // LTD: Can't convert type specification. and(number, not(complex))) & negative?(second(x))) if (second(x) = -1 & empty?(cdddr(x))) minusformatter(third(x)); else // else modify its lisp form to suit the routine minusformatter(apply(list, #"times", - cadr(x), tail(tail(x)))); end if; // if no negative number, proceed as usual else for (args = cdr(x) then cdr(args), until empty?(args)) ret_format := buildformat(head(args)); // count minuses, extract +ve form from it if necessary // is this necessary?? rjf 1/91 // (format t "~%testing ~s" (formatstruct-op ret_format)) if (~ not(instance?(ret_format, )) & ret_format.formatstruct-op == #"minus") ret_format := second(ret_format.formatstruct-ls); signs := signs + 1; elseif (nil); end if; if (not(instance?(ret_format, ))) inc!(width, atomwidth(ret_format) + 1); else if (lessprec(ret_format, #"times")) ret_format := parenformatter(ret_format); end if; inc!(width, 1 + ret_format.formatstruct-width); heightpacket := topboths(ret_format, maxtoph, maxbotmh); maxtoph := head(heightpacket); maxbotmh := tail(heightpacket); inc!(hoffset, ret_format.formatstruct-hoffset); voffset := max(voffset, ret_format.formatstruct-voffset); end if; ls := apply(list, ret_format, " ", ls); finally // if odd number of minuses is encountered in this times exp // then pass to minusformatter if (odd?(signs)) let ret_form = makehform(width - 1, // adjust spacing maxtoph + maxbotmh, hoffset, voffset, #"times", prec(#"times"), #"none", tail(reverse!(ls))); if (lessprec(ret_form, #"minus")) ret_form := parenformatter(ret_form); end if; makehform(width + 2, maxtoph + maxbotmh, hoffset, voffset, #"minus", prec(#"minus"), #"left", list("-", ret_form)); else makehform(width - 1, // adjust spacing maxtoph + maxbotmh, hoffset, voffset, #"times", prec(#"times"), #"none", tail(reverse!(ls))); end if; end for; end if; end method timesformatter; // TimesReorg: // Reorganize the times expression to form a proper // Divide expression (Times exp (power exp -1)) if // necessary. define method timesreorg (x) x; end method timesreorg; // leave arg alone. // This was written by Derek Lai // keep this in reserve. I think it's unnecessary -- RJF. // // powerformatter: // This function takes care of cases like e^x. It takes (POWER E X) // and returns // // (HORIZONTALFORM 2 2 0 0 POWER 190 RIGHT // (E (VERTICALFORM 1 1 0 -1 SUPERSCRIPT 260 NONE X))) nil(#f, nil(), nil((nil(nil(nil(#f))))(nil(nil(nil(#f))), nil(#f)), nil(nil(#f), nil(#f, nil(nil(#f), #f, #f, #f, #(), nil(#()), #(), #f)), nil(#f, nil(nil(#f), nil(#f), #f, #f, #(), nil(#()), #(), #f))), nil((nil(#f))(nil(nil(nil(#f), nil(#f)), nil(#f, nil(#f)), #f, #f, #(), nil(#()), #(), #(#(), #()))), nil(nil((nil(nil(#f)))(), // find out whether expo is higher than numerator nil((nil(nil(nil(nil(#f), nil(#f)), nil(#f, nil(#f)))))(), nil(nil(nil(#f), nil(#f)), nil(#f, nil(#f)), #f, nil(#f), #(), nil(#()), #(), #(#(), #())))))))); // realformatter: // This function takes a Lisp prefix form of a real number // and convert it to a real number string. define method realformatter (x) let p1 = second(x); let p2 = third(x); let temp = format(#f, "%D.%D", p1, p2); temp; end method realformatter; // parenformatter: // Wrap a given formatstruct with parentheses. define method parenformatter (f) makehform(f.formatstruct-width + 2, f.formatstruct-height, 0, f.formatstruct-voffset, #"sequence", prec(#"sequence"), #"none", list("(", f, ")")); end method parenformatter; // minusformatter: // This function takes X and makes // a horizontal minus form (that is, for - X). // // (HORIZONTALFORM 2 1 0 0 MINUS 170 LEFT ("-" X)) define method minusformatter (x) let ret_form = buildformat(x); if (not(instance?(ret_form, ))) makehform(1 + atomwidth(ret_form), 1, 0, 0, #"minus", prec(#"minus"), #"left", list("-", ret_form)); else if (prec(ret_form.formatstruct-op) < prec(#"minus")) ret_form := parenformatter(ret_form); end if; makehform(1 + ret_form.formatstruct-width, ret_form.formatstruct-height, 0, ret_form.formatstruct-voffset, #"minus", prec(#"minus"), #"left", list("-", ret_form)); end if; end method minusformatter; // IsDivide: // Checks if a Lisp prefix form expression in fact // corresponds to a division expression. // For example, IsDivide returns TRUE with the following // input expression: // // (TIMES A (POWER B -1)) define method isdivide (x) if (size(x) ~= 3) #f; else let bool = #f; let p1 = second(x); let p2 = third(x); if (~ not(instance?(p2, ))) if (head(p2) = #"power" & third(p2) = -1) bool := #t; end if; end if; // the first part of x must be a non -1 power if (~ not(instance?(p1, ))) if (head(p1) = #"power" & third(p1) = -1) bool := #f; end if; end if; bool; end if; end method isdivide; // divideformatter: // In general, after a given Lisp prefix is identified as // a division expression by the function "IsDivide" (above), // it is passed to divideformatter. For example, it converts // (TIMES A (POWER B -1)) to // // (VERTICALFORM 1 3 0 1 DIVIDE 160 LEFT // (A (REPEATEDSTRING "-" 1) B)) define method divideformatter (x) let p1 = second(x); let p2 = third(x); let retf1 = buildformat(p1); let retf2 = begin let temp = buildformat(second(p2)); if (divideformp(temp)) // due to associativity parenformatter(temp); else temp; end if; end; let w1 = formatwidth(retf1); let w2 = formatwidth(retf2); let barwidth = max(w1, w2); // stuff space if (abs(w1 - w2) > 1) if (w1 > w2) retf2 := stuffspace(retf2, barwidth); else retf1 := stuffspace(retf1, barwidth); end if; end if; makevform(barwidth, 1 + formatheight(retf1) + formatheight(retf2), 0, formatheight(retf2), #"divide", prec(#"divide"), #"left", list(retf1, makerepstr(barwidth, "-"), retf2)); end method divideformatter; // handle complex numbers only. // perhaps this should be user-defined? (Could be over-ridden by // an appropriate mechanism like ComplexFormatter[] ... ) define method complexnumformatter (x) let imag = if (imag-part(x) = 0) #f; elseif (imag-part(x) = 1) #"i"; else apply(list, #"times", imag-part(x), #(#"i")); end if; buildformat(if (empty?(imag)) real-part(x); elseif (0 = real-part(x)) imag; else list(#"plus", real-part(x), imag); end if); end method complexnumformatter; // handle the output of "Set" forms symbol-get-property(#"set", #"stringsym") := " = "; symbol-get-property(#"set", #"stringsymlen") := 3; // based on dotformatter define method setformatter (x) let width = 0; let maxtoph = 1; let maxbotmh = 0; let hoffset = 0; let voffset = 0; let ls = #f; let heightpacket = #f; let ret_format = #f; for (args = cdr(x) then cdr(args), until empty?(args)) ret_format := buildformat(head(args)); if (not(instance?(ret_format, ))) inc!(width, atomwidth(ret_format) + 3); ls := apply(list, ret_format, " = ", ls); else if (lessprec(ret_format, #"set")) ret_format := parenformatter(ret_format); end if; heightpacket := topboths(ret_format, maxtoph, maxbotmh); maxtoph := head(heightpacket); maxbotmh := tail(heightpacket); inc!(width, ret_format.formatstruct-width + 3); voffset := max(voffset, ret_format.formatstruct-voffset); inc!(hoffset, ret_format.formatstruct-hoffset); ls := apply(list, ret_format, " = ", ls); end if; finally makehform(width - 3, maxtoph + maxbotmh, hoffset, voffset, #"set", prec(#"set"), #"none", tail(reverse!(ls))); end for; end method setformatter; // we can do this for now, since the results are similar // for formatting + and *. However the precedences are different. // also, perhaps the formatting symbol for Times is merely adjacency. // StuffSpace: // This function puts in spaces as, say, // (RepeatedString " " 6). define method stuffspace (f, framewidth) let numspace = floor/(framewidth - formatwidth(f), 2); makehform(formatwidth(f) + numspace, formatheight(f), 0, if (not(instance?(f, ))) 0; else f.formatstruct-voffset; end if, #"sequence", prec(#"sequence"), #"none", list(makerepstr(numspace, " "), f)); end method stuffspace; // MakeRepStr: // It constructs repeated strings. define method makerepstr (x, symb) list(#"repeatedstring", symb, x); end method makerepstr;