File r38/packages/rprint/rprint.red artifact bb85d4b489 part of check-in 3c4d7b69af


module rprint;  % The Standard LISP to REDUCE pretty-printer.

% Author: Anthony C. Hearn.
% Modifications by: Francis J. Wright.

create!-package('(rprint),'(util));

fluid '(!*lower !*n buffp combuff!* curmark curpos orig pretop
	pretoprinf rmar rprifn!* rterfn!*);

comment RPRIFN!* allows output from RPRINT to be handled differently,
        RTERFN!* allows end of lines to be handled differently;

fluid '(!*rprint!-downcase);

!*rprint!-downcase := nil;

pretop := 'op; pretoprinf := 'oprinf;

symbolic procedure rprint u;
   begin integer !*n; scalar buff,buffp,curmark,rmar,x;
      curmark := 0;
      buff := buffp := list list(0,0);
      rmar := linelength nil;
      x := get('!*semicol!*,pretop);
      !*n := 0;
      mprino1(u,list(caar x,cadar x));
      prin2ox ";";
      omarko curmark;
      prinos buff
   end;

symbolic procedure rprin1 u;
   begin scalar buff,buffp,curmark,x;
      curmark := 0;
      buff := buffp := list list(0,0);
      x := get('!*semicol!*,pretop);
      mprino1(u,list(caar x,cadar x));
      omarko curmark;
      prinos buff
   end;

symbolic procedure mprino u; mprino1(u,list(0,0));

symbolic procedure mprino1(u,v);
   begin scalar x;
	if x := atsoc(u,combuff!*)
          then <<for each y in cdr x do comprox y;
		 combuff!* := delete(x,combuff!*)>>;
      if numberp u and u<0 and (x := get('difference,pretop))
        then return begin scalar p;
        x := car x;
	p := not(car x>cadr v) or not(cadr x>car v);
        if p then prin2ox "(";
        prinox u;
        if p then prinox ")"
       end
       else if atom u then return prinox u
      else if not atom car u and (x:=strangeop u)
          then return mprino1(x,v)
      else if not atom car u 
           then <<curmark := curmark+1;
          prin2ox "("; mprino car u; prin2ox ")";
	  omark list(curmark,3); curmark := curmark - 1>>
       else if x := get(car u,pretoprinf)
        then return begin scalar p;
           p := car v>0
		 and not(car u memq
			   '(list procedure prog quote rblock string));
           if p then prin2ox "(";
	   apply1(x,cdr u);
           if p then prin2ox ")"
         end
       else if x := get(car u,pretop)
        then return if car x then inprinox(u,car x,v)
% Next line commented out since not all user infix operators are binary.
%                    else if cddr u then rederr "Syntax error"
                     else if null cadr x then inprinox(u,list(100,1),v)
                     else inprinox(u,list(100,cadr x),v)
       else if flagp(car u,'modefn) and eqcar(cadr u,'procedure)
	then return proceox(cadadr u . car u . cdr cddadr u)
       else prinox car u;
      if rlistatp car u then return rlpri cdr u;
      u := cdr u;
      if null u then prin2ox "()"
      else mprargs(u,v)
   end;

symbolic procedure strangeop u;
  % U is a non-atomic operator; try to find a better print form for it.
  % The commented definition doesn't check the complexity of the
  % argument, and so can lead to more computation.
% if caar u='lambda and length cadar u=1 then
%   subst(cadr u,car cadar u,car cddar u);
   nil;

symbolic procedure mprargs(u,v);
   if null cdr u then <<prin2ox " "; mprino1(car u,list(100,100))>>
   else inprinox('!*comma!* . u,list(0,0),v);

symbolic procedure inprinox(u,x,v);
   begin scalar p;
      p := not(car x>cadr v) or not(cadr x>car v);
      if p then prin2ox "("; omark '(m u);
      inprino(car u,x,cdr u);
      if p then prin2ox ")"; omark '(m d)
   end;

symbolic procedure inprino(opr,v,l);
   begin scalar flg,x;
      curmark := curmark+2;
      x := get(opr,pretop);
      if x and car x
        then <<mprino1(car l,list(car v,0)); l := cdr l; flg := t>>;
      while l do
        <<if opr eq '!*comma!* then <<prin2ox ","; omarko curmark>>
           else if opr eq 'setq
            then <<prin2ox " := "; omark list(curmark,1)>>
	else if atom car l or not(opr eq get(caar l,'alt))
        then <<omark list(curmark,1); oprino(opr,flg); flg := t>>;
      mprino1(car l,list(if null cdr l then 0 else car v,
                          if null flg then 0 else cadr v));
         l := cdr l>>;
      curmark := curmark - 2
   end;

symbolic procedure oprino(opr,b);
   (lambda x; if null x
                 then <<if b then prin2ox " "; prinox opr; prin2ox " ">>
	       else <<if y then prin2ox " "; prin2ox x;
		      if y then prin2ox " ">>
		     where y = flagp(opr,'spaced))
   get(opr,'prtch);

flag('(cons),'spaced);

flag('(add mult over to),'spaced);  % So that we don't have 1./1 etc.

symbolic procedure prin2ox u;
   <<rplacd(buffp,explodex u);
     while cdr buffp do buffp := cdr buffp>>;

symbolic procedure explodex u;
   % "Explodes" atom U without including escape characters.
   if numberp u then explode u
    else if stringp u then reversip cdr reversip cdr explode u
    else explodex1 explode u;

symbolic procedure explodex1 u;
   if null u then nil
    else if car u eq '!! then cadr u . explodex1 cddr u
    else check!-downcase car u . explodex1 cdr u;

symbolic procedure explodey u;
   begin scalar v;
      v := explode u;
      if idp u then v := for each x in v collect check!-downcase x;
      return v
   end;

symbolic procedure check!-downcase u;
   begin scalar z;
      return if !*rprint!-downcase
                and liter u
		and (z := atsoc(u,
		     '((!A . !a) (!B . !b) (!C . !c) (!D . !d) (!E . !e)
		       (!F . !f) (!G . !g) (!H . !h) (!I . !i) (!J . !j)
		       (!K . !k) (!L . !l) (!M . !m) (!N . !n) (!O . !o)
		       (!P . !p) (!Q . !q) (!R . !r) (!S . !s) (!T . !t)
		       (!U . !u) (!V . !v) (!W . !w) (!X . !x) (!Y . !y)
		       (!Z . !z))))
	       then cdr z
	      else u
   end;

symbolic procedure prinox u;
   <<if x then u := x;
     rplacd(buffp,explodey u);
     while cdr buffp do buffp := cdr buffp>>
    where x = get(u,'oldnam);

symbolic procedure omark u;
   <<rplacd(buffp,list u); buffp := cdr buffp>>;

symbolic procedure omarko u; omark list(u,0);

symbolic procedure comprox u;
   begin scalar x;
        if car buffp = '(0 0)
          then return <<for each j in u do prin2ox j;
                        omark '(0 0)>>;
        x := car buffp;
        rplaca(buffp,list(curmark+1,3));
        for each j in u do prin2ox j;
        omark x
   end;

symbolic procedure rlistatp u;
   get(u,'stat) member '(endstat rlis);

symbolic procedure rlpri u;
   if null u then nil
    else begin
      prin2ox " ";
      omark '(m u);
      inprino('!*comma!*,list(0,0),u);
      omark '(m d)
   end;

symbolic procedure condox u;
   begin scalar x;
      omark '(m u);
      curmark := curmark+2;
      while u do
	<<prin2ox "if "; mprino caar u; omark list(curmark,1);
	  prin2ox " then ";
          if cdr u and eqcar(cadar u,'cond)
                 and not eqcar(car reverse cadar u,'t)
           then <<x := t; prin2ox "(">>;
          mprino cadar u;
          if x then prin2ox ")";
          u := cdr u;
	  if u then <<omarko(curmark - 1); prin2ox " else ">>;
          if u and null cdr u and caar u eq 't
            then <<mprino cadar u; u := nil>>>>;
      curmark := curmark - 2;
      omark '(m d)
   end;

put('cond,pretoprinf,'condox);

symbolic procedure blockox u;
   begin
      omark '(m u);
      curmark := curmark+2;
      prin2ox "begin ";
      if car u then varprx car u;
      u := labchk cdr u;
      omark list(curmark,if eqcar(car u,'!*label) then 1 else 3);
      while u do
        <<mprino car u;
        if not eqcar(car u,'!*label) and cdr u then prin2ox "; ";
        u := cdr u;
        if u
          then omark list(curmark,
                          if eqcar(car u,'!*label) then 1 else 3)>>;
      omark list(curmark - 1,-1);
      prin2ox " end";
      curmark := curmark - 2;
      omark '(m d)
   end;

symbolic procedure retox u;
   begin
      omark '(m u);
      curmark := curmark+2;
      prin2ox "return ";
      omark '(m u);
      mprino car u;
      curmark := curmark - 2;
      omark '(m d);
      omark '(m d)
   end;

put('return,pretoprinf,'retox);

symbolic procedure varprx u;
   begin scalar typ;
       while u do
        <<if cdar u eq typ
            then <<prin2ox ","; omarko(curmark+1); prinox caar u>>
           else <<if typ then <<prin2ox "; "; omark '(m d)>>;
                prinox (typ := cdar u);
                  prin2ox " "; omark '(m u); prinox caar u>>;
           u := cdr u>>;
      prin2ox "; ";
      omark '(m d)
   end;

put('rblock,pretoprinf,'blockox);

symbolic procedure progox u;
% This used to say "reverse car u" but ACN has unilaterally changed it.
   blockox((for each j in car u collect j . 'scalar) . cdr u);

symbolic procedure labchk u;
   begin scalar x;
      for each z in u do if atom z
        then x := list('!*label,z) . x else x := z . x;
       return reversip x
   end;

put('prog,pretoprinf,'progox);

symbolic procedure gox u;
   <<prin2ox "go to "; prinox car u>>;

put('go,pretoprinf,'gox);

symbolic procedure labox u;
   <<prinox car u; prin2ox ": ">>;

put('!*label,pretoprinf,'labox);

symbolic procedure quotoxx u;
   begin
      if stringp u then return prinox u;
      prin2ox "'";
      u := car u;
      if atom u then return prinox u;
      curmark := curmark+1;
      prin2ox "(";
      omark '(m u);
  a:  if atom u then <<prin2ox " . "; prinox u; u := nil>>
       else <<mprino car u; u := cdr u;
	      if u then <<prin2ox '! ; omarko curmark>>>>;
      if u then go to a;
      omark '(m d);
      prin2ox ")";
      curmark := curmark - 1
   end;

symbolic procedure quotox u;
   if stringp u then prinox u else <<prin2ox "'"; prinsox car u>>;

symbolic procedure prinsox u;
   if atom u then prinox u
    else <<curmark := curmark+1;
	   prin2ox "(";
           omark '(m u);
	   while u do <<prinsox car u;
			u := cdr u;
			if u then <<omark list(curmark,-1);
			if atom u
			  then <<prin2ox " . "; prinsox u; u := nil>>
			 else prin2ox " ">>>>;
	   omark '(m d);
	   prin2ox ")";
	   curmark := curmark - 1>>;

put('quote,pretoprinf,'quotox);

symbolic procedure prognox u;
   begin
      curmark := curmark+1;
      prin2ox "<<";
      omark '(m u);
      while u do <<mprino car u; u := cdr u;
                if u then <<prin2ox "; "; omarko curmark>>>>;
      omark '(m d);
      prin2ox ">>";
      curmark := curmark - 1
   end;

put('prog2,pretoprinf,'prognox);

put('progn,pretoprinf,'prognox);

symbolic procedure listox u;
   begin
      curmark := curmark+1;
      prin2ox "{";
      omark '(m u);
      while u do <<mprino car u; u := cdr u;
%               if u then <<prin2ox ", "; omarko curmark>>>>;
		if u then <<prin2ox ","; omarko curmark>>>>;
      omark '(m d);
      prin2ox "}";
      curmark := curmark - 1
   end;

put('list,pretoprinf,'listox);

symbolic procedure repeatox u;
   begin
      curmark := curmark+1;
      omark '(m u);
      prin2ox "repeat ";
      mprino car u;
      prin2ox " until ";
      omark list(curmark,3);
      mprino cadr u;
      omark '(m d);
      curmark := curmark - 1
   end;

put('repeat,pretoprinf,'repeatox);

symbolic procedure whileox u;
   begin
      curmark := curmark+1;
     omark '(m u);
      prin2ox "while ";
      mprino car u;
      prin2ox " do ";
      omark list(curmark,3);
      mprino cadr u;
      omark '(m d);
      curmark := curmark - 1
   end;

put('while,pretoprinf,'whileox);

symbolic procedure procox u;
   begin
      omark '(m u);
      curmark := curmark+1;
      if cadddr cdr u then <<mprino cadddr cdr u; prin2ox " ">>;
      prin2ox "procedure ";
      procox1(car u,cadr u,caddr u)
   end;

symbolic procedure procox1(u,v,w);
   begin
      prinox u;
      if v then mprargs(v,list(0,0));
      prin2ox "; ";
      omark list(curmark,3);
      mprino w;
      curmark := curmark - 1;
      omark '(m d)
   end;

put('proc,pretoprinf,'procox);

symbolic procedure proceox u;
   begin
      omark '(m u);
      curmark := curmark+1;
      if cadr u then <<mprino cadr u; prin2ox " ">>;
      if not(caddr u eq 'expr) then <<mprino caddr u; prin2ox " ">>;
      prin2ox "procedure ";
      proceox1(car u,cadddr u,car cddddr u)
   end;

symbolic procedure proceox1(u,v,w);
   % Prettyprint the procedure's argument list, any active annotation,
   % and its body.
   begin scalar annot;
      prinox u;
      if v
	then <<if not atom car v then v := mapovercar v;
               %allows for typing to be included with proc arguments;
               mprargs(v,list(0,0))>>;
      prin2ox "; ";
      if annot := get(u,'active!-annotation) then
	    <<omark list(curmark,3);
	      prin2ox "/* ";
	      princom car annot;
	      prin2ox " */";
	      omark '(m d)>>;
      omark list(curmark,3);
      mprino w;
      curmark := curmark - 1;
      omark '(m d)
   end;

put('procedure,pretoprinf,'proceox);

symbolic procedure proceox0(u,v,w,x);
   proceox list(u,'symbolic,v,for each j in w collect j . 'symbolic,x);

symbolic procedure deox u;
   proceox0(car u,'expr,cadr u,caddr u);

put('de,pretoprinf,'deox);

% symbolic procedure dfox u;
%   proceox0(car u,'fexpr,cadr u,caddr u);

%put('df,pretoprinf,'dfox);  % Commented out because of confusion with
			     % differentiation.  We also want to
			     % discourage use of fexpr in REDUCE.

symbolic procedure dsox u;
   proceox0(car u,'smacro,cadr u,caddr u);

put('ds,pretoprinf,'dsox);

symbolic procedure stringox u;
   <<prin2ox '!"; prin2ox car u; prin2ox '!">>;

put('string,pretoprinf,'stringox);

symbolic procedure lambdox u;
   begin
      omark '(m u);
      curmark := curmark+1;
      procox1('lambda,car u,cadr u)
   end;

put('lambda,pretoprinf,'lambdox);

symbolic procedure eachox u;
   <<prin2ox "for each ";
     while cdr u do <<mprino car u; prin2ox " "; u := cdr u>>;
     mprino car u>>;

put('foreach,pretoprinf,'eachox);

symbolic procedure forox u;
   begin
      curmark := curmark+1;
      omark '(m u);
      prin2ox "for ";
      mprino car u;
      prin2ox " := ";
      mprino caadr u;
      if cadr cadr u neq 1
	then <<prin2ox " step "; mprino cadr cadr u; prin2ox " until ">>
       else prin2ox ":";
      mprino caddr cadr u;
      prin2ox " ";
      mprino caddr u;
      prin2ox " ";
      omark list(curmark,3);
      mprino cadddr u;
      omark '(m d);
      curmark := curmark - 1
   end;

put('for,pretoprinf,'forox);

symbolic procedure forallox u;
   begin
      curmark := curmark+1;
      omark '(m u);
      prin2ox "for all ";
      inprino('!*comma!*,list(0,0),car u);
      if cadr u
        then <<omark list(curmark,3);
	       prin2ox " such that ";
               mprino cadr u>>;
      prin2ox " ";
      omark list(curmark,3);
      mprino caddr u;
      omark '(m d);
      curmark := curmark - 1
   end;

put('forall,pretoprinf,'forallox);

comment Support for printing algebraic mode code;

put('aeval!*,pretoprinf,'aevalox);

put('aeval,pretoprinf,'aevalox);

put('revalx,pretoprinf,'aevalox);       % FJW.

symbolic procedure aevalox(u); 
      mprino aevalox1 car u;

symbolic procedure aevalox1 u;
  % unquote and listify.
  if eqcar(u,'quote) then cadr u else
  if eqcar(u,'list) then
    for each q in u collect aevalox1 q else u;

symbolic procedure minuspox u;
  if eqcar(car u,'difference) then
     mprino('lessp.cdar u) else mprino('lessp.car u.'(0));

put('minusp,pretoprinf,'minuspox);
put('aminusp!:,pretoprinf,'minuspox);
put('evalequal,pretoprinf,function (lambda u;mprino('equal.u)));
put('evalgreaterp,pretoprinf,function (lambda u;mprino('greaterp.u)));
put('evalgeq,pretoprinf,function (lambda u;mprino('geq.u)));
put('evallessp,pretoprinf,function (lambda u;mprino('lessp.u)));
put('evalleq,pretoprinf,function (lambda u;mprino('leq.u)));
put('evalneq,pretoprinf,function (lambda u;mprino('neq.u)));
put('!:dn!:,pretoprinf,function (lambda u;
                mprino(float car u*expt(float 10,cdr u))));
put('!:rd!:,pretoprinf,function (lambda u;
                mprino(if atom u then u else 
                        float car u*expt(float 2,cdr u))));
put('plus2,pretoprinf,function(lambda u;mprino('plus.u)));


comment Declarations needed by old parser;

if null get('!*semicol!*,'op)
  then <<put('!*semicol!*,'op,'((-1 0)));
         put('!*comma!*,'op,'((5 6)))>>;

% Code for printing active comments.

symbolic procedure princom u;
   % Print an active comment.
   begin scalar w,x,y,z; integer n;
      x := explode2 u;
      % Process first line.
      while car x eq '!  do x := cdr x;
      while x and car x neq !$eol!$ do <<y := car x . y; x := cdr x>>;
      while y and car y eq '!  do y := cdr y;
      w := reversip!* y;    % Header line.
      % Process remaining lines.
      while x and (x := cdr x) do
       <<y := nil;
	 n := 0;
	 while car x eq '!  do <<x := cdr x; n := n+1>>;
	 while x and car x neq !$eol!$ do <<y := car x . y; x:= cdr x>>;
	 while y and car y eq '!  do y := cdr y;
	 z := (n . reversip!* y) . z>>;
      % Find line with least blanks.
      y := z;
      if y
	then <<n := caar y; while (y := cdr y) do n := min(n,caar y)>>;
      while z do <<y := addblanks(cdar z,caar z - n) . y; z := cdr z>>;
      % Now merge lines where possible.
      while y do
       <<z := car y;
	 if not(car z eq '!  ) and not(car w eq '! )
	   then <<z := '!  . z; w := nconc!*(w,z)>>
	  else <<x := w . x; w := z>>;
	 y := cdr y>>;
      x := w . x;
      % Final processing.
      x := reversip!* x;
      while x do <<addmarks car x; x := cdr x;
		   if x then omark list(curmark,3)>>
   end;

symbolic procedure addblanks(u,n);
   if n=0 then u else '!  . addblanks(u,n - 1);

symbolic procedure addmarks u;
   begin scalar bool,x;
      while u do
	 <<if car u eq '!  then (if null bool
		then <<bool := t; x := {'M,'L} . x>>)
	       else bool := nil;
	   x := car u . x; u := cdr u>>;
      rplacd(buffp,reversip!* x);
      while cdr buffp do buffp := cdr buffp
   end;


comment RPRINT MODULE, Part 2;

fluid '(orig curpos);

symbolic procedure prinos u;
   begin integer curpos;
	scalar !*lower,orig;
      orig := list posn();
      curpos := car orig;
      prinoy(u,0);
      terpri0x()
   end;

symbolic procedure prinoy(u,n);
   begin scalar x;
      if car(x := spaceleft(u,n)) then return prinom(u,n)
       else if null cdr x then return if car orig<10 then prinom(u,n)
       else <<orig := 9 . cdr orig;
                terpri0x();
                spaces20x(curpos := 9+cadar u);
                prinoy(u,n)>>
      else begin
        a: u := prinoy(u,n+1);
           if null cdr u or caar u<=n then return;
           terpri0x();
           spaces20x(curpos := car orig+cadar u);
           go to a end;
      return u
   end;

symbolic procedure spaceleft(u,mark);
   %U is an expanded buffer of characters delimited by non-atom marks
   %of the form: '(M ...) or '(INT INT))
   %MARK is an integer;
   begin integer n; scalar flg,mflg;
      n := rmar - curpos;
      u := cdr u;   %move over the first mark;
      while u and not flg and n>=0 do
	<<if atom car u then n := n - 1
           else if caar u eq 'm then nil
           else if mark>=caar u then <<flg := t; u := nil . u>>
           else mflg := t;
          u := cdr u>>;
      return ((n>=0) . mflg)
   end;

symbolic procedure prinom(u,mark);
   begin integer n; scalar flg,x;
      n := curpos;
      u := cdr u;
      while u and not flg do
        <<if atom car u then <<x := prin20x car u; n := n+1>>
          else if caar u eq 'm
           then if cadar u eq 'u then orig := n . orig
		 else if cadar u eq 'l
		  then (if chars2 cdr u > (rmar - posn())
			  then <<terpri0x(); spaces20x(curmark+5)>>)
                 else orig := cdr orig
	   % Check for long thin lists.
           else if mark>=caar u
	      and not(x memq '(!,) % '(!, ! )
		       and rmar - n - 6>charspace(u,x,mark))
            then <<flg := t; u := nil . u>>;
          u := cdr u>>;
      curpos := n;
        if mark=0 and cdr u
          then <<terpri0x();
%                terpri0x();
                 orig := list 0; curpos := 0; prinoy(u,mark)>>;
          %must be a top level constant;
      return u
   end;

symbolic procedure chars2 u; chars21(u,0);

symbolic procedure chars21(u,n);
   if eqcar(car u,'m) then n else chars21(cdr u,n+1);

symbolic procedure charspace(u,char,mark);
   % Determines if there is space until the next character CHAR.
   begin integer n;
      n := 0;
      while u do
        <<if car u = char then u := list nil
           else if atom car u then n := n+1
           else if car u='(m u) then <<n := 1000; u := list nil>>
           else if numberp caar u and caar u<mark then u := list nil;
          u := cdr u>>;
      return n
   end;

symbolic procedure spaces20x n;
   %for i := 1:n do prin20x '! ;
   while n>0 do <<prin20x '! ; n := n - 1>>;

symbolic procedure prin2rox u;
   begin integer m,n; scalar x,y;
      m := rmar - 12;
      n := rmar - 1;
      while u do
        if car u eq '!"
	  then <<if not stringspace(cdr u,n - !*n)
                   then <<terpri0x(); !*n := 0>>
                  else nil;
                 prin20x '!";
                 u := cdr u;
		 while not(car u eq '!") do
                   <<prin20x car u; u := cdr u; !*n := !*n+1>>;
                 prin20x '!";
                 u := cdr u;
                 !*n := !*n+2;
                 x := y := nil>>
         else if atom car u and not(car u eq '!  and (!*n=0 or null x
	      or cdr u and breakp cadr u or breakp x and not(y eq '!!)))
          then <<y := x; prin20x(x := car u); !*n := !*n+1;
         u := cdr u;
	 if !*n=n or !*n>m and not breakp car u and nospace(u,n - !*n)
          then <<terpri0x(); x := y := nil>> else nil>>
         else u := cdr u
   end;

symbolic procedure nospace(u,n);
   if n<1 then t
    else if null u then nil
    else if not atom car u then nospace(cdr u,n)
    else if not(car u eq '!!) and (cadr u eq '!  or breakp cadr u)
     then nil
    else nospace(cdr u,n - 1);

symbolic procedure breakp u;
   u member '(!< !> !; !: != !) !+ !- !, !' !");

symbolic procedure stringspace(u,n);
   if n<1 then nil else car u eq '!" or stringspace(cdr u,n - 1);


comment Some interfaces needed;

symbolic procedure prin20x u;
   if rprifn!* then apply1(rprifn!*,u) else prin2 u;

symbolic procedure terpri0x;
   if rterfn!* then lispeval {rterfn!*} else terpri();

endmodule;

end;


REDUCE Historical
REDUCE Sourceforge Project | Historical SVN Repository | GitHub Mirror | SourceHut Mirror | NotABug Mirror | Chisel Mirror | Chisel RSS ]