/// <summary>Handles Syntax.K.{BinLeft,BinRight,BinAlone}</summary>
        /// <param name="prec">precedence of operator</param>
        /// <param name="assoc">BinLeft, BinRight, or BinAlone</param>
        private List <T> Translate_thing(Expr head, Expr[] args, Syntax.WOrC op, Syntax.TType type, int prec, Syntax.K assoc)
        {
            Trace.Assert(args.Length == 2);
            List <T> lt = new List <T>();

            lt.Add(Translate_maybe_paren(args[0], prec - ((assoc == Syntax.K.BinLeft) ? 1 : 0)));
            lt.Add(__TranslateOperator(head, op, type));
            lt.Add(Translate_maybe_paren(args[1], prec - ((assoc == Syntax.K.BinRight) ? 1 : 0)));
            return(lt);
        }
        /// <summary>Handles Syntax.K.BinAllOfLike</summary>
        /// <param name="prec">precedence of operator</param>
        private List <T> Translate_multithing(Expr bin, Expr[] args, Syntax.WOrC op, Syntax.TType type, int prec)
        {
            Trace.Assert(args.Length >= 2);
            List <T> lt = new List <T>();

            lt.Add(Translate_maybe_paren(args[0], prec));
            for (int i = 1; i < args.Length; i++)
            {
                lt.Add(__TranslateOperator(bin, i - 1, op, type));
                lt.Add(Translate_maybe_paren(args[i], prec));
            }
            return(lt);
        }
Exemple #3
0
        protected override Box __TranslateWord(Expr expr, string op, Syntax.TType type)
        {
            /* This is really a hack :-( */
            StringBox sb = new StringBox(expr, op, type);

            if (type == Syntax.TType.LargeOp && Array.BinarySearch(limitwordops, op) < 0)
            {
                return(new AtomBox(null, sb, null, null, Syntax.TType.LargeOp, AtomBox.LimitsType.NoLimits));
            }
            else
            {
                return(sb);
            }
        }
        /// <summary>
        /// Handles Syntax.K.{Prefix,Postfix}
        /// </summary>
        /// <param name="side">Prefix or Postfix</param>
        /// <returns></returns>
        private List <T> Translate_prepostfix(Expr head, Expr arg, Syntax.WOrC op, Syntax.TType type, int prec, Syntax.K side)
        {
            List <T> lt = new List <T>();

            if (side == Syntax.K.Prefix || side == Syntax.K.PrefixOpt)
            {
                lt.Add(__TranslateOperator(head, op, type));
            }
            lt.Add(Translate_maybe_paren(arg, prec - 1));
            if (side == Syntax.K.Postfix)
            {
                lt.Add(__TranslateOperator(head, op, type));
            }
            return(lt);
        }
        private T _Translate(WellKnownSym s)
        {
            switch (s.ID)
            {
            case WKSID.del:
                return(__TranslateOperator(s, Unicode.N.NABLA, Syntax.TType.Ord));

            case WKSID.differentiald:
                return(__TranslateOperator(s, Unicode.D.DOUBLE_STRUCK_ITALIC_SMALL_D, Syntax.TType.Ord));

            case WKSID.partiald:
                return(__TranslateOperator(s, Unicode.P.PARTIAL_DIFFERENTIAL, Syntax.TType.Ord));

            case WKSID.integral:
                return(__TranslateOperator(s, Unicode.I.INTEGRAL, Syntax.TType.LargeOp));

            case WKSID.i:
                return(__TranslateOperator(s, Unicode.D.DOUBLE_STRUCK_ITALIC_SMALL_I, Syntax.TType.Ord));

            case WKSID.e:
                return(__TranslateOperator(s, Unicode.D.DOUBLE_STRUCK_ITALIC_SMALL_E, Syntax.TType.Ord));

            default:
                KeyValuePair <Syntax.WOrC, Syntax.TType>?val;
                val = Syntax.CharWKSMap[s];
                if (val == null)
                {
                    Syntax.OpRelPos orp = Syntax.Fixes.Find(s);
                    if (orp.Precedence == -1)
                    {
                        return(__TranslateWord(s, s.ID.ToString(), Syntax.TType.LargeOp));
                    }
                    else
                    {
                        Syntax.OpRel or = Syntax.Fixes.Table[orp.Precedence];
                        Syntax.WOrC  op = or.Ops[orp.Position];
                        Syntax.TType tt = or.Types[orp.Position];
                        return(__TranslateOperator(s, op, tt));
                    }
                }
                else
                {
                    return(__TranslateOperator(s, val.Value.Key, val.Value.Value));
                }
            }
        }
Exemple #6
0
 private Syntax.TType SetTType(Box b, Syntax.TType tt)
 {
     Trace.Assert(b != null);
     if (b is CharBox)
     {
         ((CharBox)b).TeXType = tt;
     }
     else if (b is StringBox)
     {
         ((StringBox)b).TeXType = tt;
     }
     else if (b is AtomBox)
     {
         ((AtomBox)b).TeXType = tt;
     }
     else
     {
         Trace.Assert(false, "unknown box type to set TType on");
     }
     return(tt);
 }
 protected abstract T __TranslateWord(Expr expr, string op, Syntax.TType type);
 protected abstract T __TranslateOperator(Expr expr, object exprix, Syntax.WOrC op, Syntax.TType type);
 protected T __TranslateOperator(Expr expr, Syntax.WOrC op, Syntax.TType type)
 {
     return(__TranslateOperator(expr, null, op, type));
 }
        private T _Translate(CompositeExpr e)
        {
            List <T> lt; // for local use because, sigh, c# doesn't handle this case of definitions used or duplicated across switch cases well
            T        t;  // this too, sigh

            Syntax.OpRelPos orp = Syntax.Fixes.Find(e.Head);
            if (orp.Precedence != -1)
            {
                Syntax.OpRel or = Syntax.Fixes.Table[orp.Precedence];
                Syntax.WOrC  op = or.Ops[orp.Position];
                Syntax.TType tt = or.Types[orp.Position];
                switch (or.Kind)
                {
                case Syntax.K.BinLeft:
                case Syntax.K.BinRight:
                case Syntax.K.BinAlone:
                    Trace.Assert(e.Args.Length == 2);
                    return(__WrapTranslatedExpr(e, Translate_thing(e.Head, e.Args, op, tt, orp.Precedence, or.Kind)));

                case Syntax.K.BinAllOfLike:
                    Trace.Assert(e.Args.Length >= 2);
                    return(__WrapTranslatedExpr(e, Translate_multithing(e, e.Args, op, tt, orp.Precedence)));

                case Syntax.K.BinPrimaryAndSecondary:
                    if (e.Head != or.Heads[0])
                    {
                        Trace.Assert(e.Args.Length == 1);
                        // Division is a special case because it can be represented as the vertical fraction representation, which is not dealt with
                        // in our syntax table.
                        if (e.Head == WellKnownSym.divide && !e.Annotations.ContainsKey("inline"))
                        {
                            return(__TranslateVerticalFraction(e, Translate(IntegerNumber.One), Translate(e.Args[0])));
                        }
                        else
                        {
                            lt = new List <T>();
                            if (or.Kind == Syntax.K.BinPrimaryAndSecondary)
                            {
                                lt.Add(Translate_maybe_paren(or.Identity, orp.Precedence));
                            }
                            lt.Add(__TranslateOperator(e.Head, op, tt));
                            lt.Add(Translate_maybe_paren(e.Args[0], orp.Precedence));
                            return(__WrapTranslatedExpr(e, lt));
                        }
                    }
                    else
                    {
                        // FIXME!!!! currently this only works for times and divide; ignores Syntax! Ideally ought to be merged w BinPriAndSec2
                        Trace.Assert(e.Args.Length > 0);
                        if (e.Args.Length == 1)
                        {
                            return(Translate(e.Args[0]));
                        }
                        else
                        {
                            List <Expr>          num         = new List <Expr>();
                            List <CompositeExpr> denom       = new List <CompositeExpr>();
                            List <CompositeExpr> denominline = new List <CompositeExpr>();
                            foreach (Expr ee in e.Args)
                            {
                                CompositeExpr ce = ee as CompositeExpr;
                                if (ce != null && ce.Head == WellKnownSym.divide)
                                {
                                    Trace.Assert(ce.Args.Length == 1);
                                    if (ce.Annotations.ContainsKey("inline"))
                                    {
                                        denominline.Add(ce);
                                    }
                                    else
                                    {
                                        denom.Add(ce);
                                    }
                                }
                                else
                                {
                                    num.Add(ee);
                                }
                            }
                            lt = new List <T>();
                            if (num.Count > 1 || (denom.Count == 0 && denominline.Count > 0))
                            {
                                AddTimes(lt, num);
                            }
                            else
                            {
                                lt.Add(Translate(num[0]));
                            }
                            if (denom.Count != 0)
                            {
                                if (num.Count == 0)
                                {
                                    lt.Add(Translate(IntegerNumber.One));
                                }
                                T nt = __WrapTranslatedExpr(null, lt);
                                lt = new List <T>();
                                if (denom.Count > 1)
                                {
                                    List <Expr> denomraw = new List <Expr>();
                                    foreach (CompositeExpr ce in denom)
                                    {
                                        denomraw.Add(ce.Args[0]);
                                    }
                                    AddTimes(lt, denomraw);
                                }
                                else
                                {
                                    lt.Add(Translate(denom[0].Args[0]));
                                }
                                T dt = __WrapTranslatedExpr(null, lt);
                                t = __TranslateVerticalFraction(e, denom[0].Head, nt, dt);
                            }
                            else
                            {
                                if (num.Count == 0)
                                {
                                    t = Translate(IntegerNumber.One);
                                }
                                else
                                {
                                    t = __WrapTranslatedExpr(e, lt);
                                }
                            }
                            if (denominline.Count > 0)
                            {
                                lt = new List <T>();
                                lt.Add(t);
                                foreach (CompositeExpr division in denominline)
                                {
                                    lt.Add(__TranslateOperator(division.Head, '/', Syntax.TType.Ord));
                                    lt.Add(Translate(division.Args[0]));
                                }
                                return(__WrapTranslatedExpr(e, lt));
                            }
                            else
                            {
                                return(t);
                            }
                        }
                    }

                case Syntax.K.BinPrimaryAndSecondary2:
                    if (e.Head != or.Heads[0])
                    {
                        Trace.Assert(e.Args.Length == 1);
                        return(__TranslateOperatorApplication(e, __TranslateOperator(e.Head, op, tt), Translate_maybe_paren(e.Args[0], orp.Precedence)));
                    }
                    else
                    {
                        Trace.Assert(e.Args.Length > 0);
                        lt = new List <T>();
                        if (e.Annotations["initial op"] != null)
                        {
                            lt.Add(__TranslateOperator(e, 0, (char)e.Annotations["initial op"], tt));
                        }
                        lt.Add(Translate_maybe_paren(e.Args[0], orp.Precedence - 1));
                        bool checkminus = (Syntax.Fixes.Find(WellKnownSym.minus).Precedence == orp.Precedence);
                        for (int i = 1; i < e.Args.Length; i++)
                        {
                            CompositeExpr   ce   = e.Args[i] as CompositeExpr;
                            Syntax.OpRelPos orp2 = new Syntax.OpRelPos(-1, -1);
                            if (ce != null)
                            {
                                orp2 = Syntax.Fixes.Find(ce.Head);
                            }
                            if (checkminus && NumberStartsWithMinus(e.Args[i]))
                            {
                                lt.Add(Translate(e.Args[i]));
                            }
                            else if (ce != null && orp2.Precedence == orp.Precedence &&
                                     or.Heads[orp.Position] != or.Heads[orp2.Position])
                            {
                                Trace.Assert(ce.Args.Length == 1);
                                lt.Add(__TranslateOperator(e, i, or.Ops[orp2.Position], tt));
                                lt.Add(Translate_maybe_paren(ce.Args[0], orp.Precedence));
                            }
                            else
                            {
                                lt.Add(__TranslateOperator(e, i, op, tt));
                                lt.Add(Translate_maybe_paren(e.Args[i], orp.Precedence - 1));
                            }
                        }
                        return(__WrapTranslatedExpr(e, lt));
                    }

                case Syntax.K.PrefixOpt:
                case Syntax.K.Prefix:
                    if (tt == Syntax.TType.LargeOp && op.Word == null)      // Only summation? This seems oddly hard-coded.
                    {
                        Trace.Assert(0 < e.Args.Length && e.Args.Length < 4);
                        return(__TranslateBigOp(e, e.Head, op.Character, e.Args.Length > 1 ? Translate(e.Args[0]) : null, e.Args.Length > 2 ? Translate(e.Args[1]) : null,
                                                Translate_maybe_paren(e.Args[e.Args.Length - 1], orp.Precedence - 1)));
                    }
                    else if (or.Kind == Syntax.K.PrefixOpt && e.Args.Length == 0)
                    {
                        return(__TranslateOperator(e.Head, op, tt));
                    }
                    else
                    {
                        //Trace.Assert(e.Args.Length == 1);//CJ disabled temporarily for site visit demo
                        return(__WrapTranslatedExpr(e, Translate_prepostfix(e.Head, e.Args[0], op, tt, orp.Precedence, or.Kind)));
                    }

                case Syntax.K.Postfix:
                    Trace.Assert(e.Args.Length == 1);
                    return(__WrapTranslatedExpr(e, Translate_prepostfix(e.Head, e.Args[0], op, tt, orp.Precedence, or.Kind)));
                }
            }
            if (e.Head is WellKnownSym)
            {
                WellKnownSym wks = (WellKnownSym)e.Head;
                WKSID        id  = wks.ID;
                switch (id)
                {
                case WKSID.im:
                    Trace.Assert(e.Args.Length == 1);
                    return(__TranslateFunctionApplication(e, __TranslateOperator(e.Head, Unicode.B.BLACK_LETTER_CAPITAL_I, Syntax.TType.Ord),
                                                          Translate_maybe_parenfn(e.Args[0], _showInvisibles)));

#if false
                case WKSID.magnitude:
                    Trace.Assert(e.Args.Length == 1);
                    return(__TranslateDelims(e, false, 0, '|', Translate(e.Args[0]), 1, '|'));
#endif
                case WKSID.magnitude:
                    if (e.Args.Length == 1)
                    {
                        return(__TranslateDelims(e, false, "|l", '|',
                                                 __WrapTranslatedExpr(null, Translate(e.Args[0])),
                                                 "|r", '|'));
                    }
                    break;

                case WKSID.power:
                    Trace.Assert(e.Args.Length == 2);
                    if (e.Args[0] is CompositeExpr && ((CompositeExpr)e.Args[0]).Head == WellKnownSym.power)
                    {
                        t = Translate_definite_paren(e.Args[0]);
                    }
                    else
                    {
                        t = Translate_maybe_parenfn(e.Args[0]);
                    }
                    return(__AddSuperscript(e, t, Translate(e.Args[1])));

                case WKSID.subscript:
                    Trace.Assert(e.Args.Length == 2);
                    if (e.Args[0] is CompositeExpr && (((CompositeExpr)e.Args[0]).Head == WellKnownSym.power ||
                                                       ((CompositeExpr)e.Args[0]).Head == WellKnownSym.subscript))
                    {
                        t = Translate_definite_paren(e.Args[0]);
                    }
                    else
                    {
                        t = Translate_maybe_parenfn(e.Args[0]);
                    }
                    return(__AddSubscript(e, t, Translate(e.Args[1])));

                case WKSID.re:
                    Trace.Assert(e.Args.Length == 1);
                    return(__TranslateFunctionApplication(e, __TranslateOperator(e.Head, Unicode.B.BLACK_LETTER_CAPITAL_R, Syntax.TType.Ord),
                                                          Translate_maybe_parenfn(e.Args[0], _showInvisibles)));

                case WKSID.root:
                    if (e.Args[0] is IntegerNumber && (e.Args[0] as IntegerNumber).Num == 2)
                    {
                        return(__TranslateRadical(e, Translate(e.Args[1]), null));
                    }
                    else
                    {
                        return(__TranslateRadical(e, Translate(e.Args[1]), Translate(e.Args[0])));
                    }

                case WKSID.differentiald:
                case WKSID.partiald:
                    Trace.Assert(e.Args.Length == 1);
                    if (e.Args[0] is Sym)
                    {
                        return(__TranslateOperatorApplication(e, Translate(e.Head), Translate(e.Args[0])));
                    }
                    else
                    {
                        return(__TranslateOperatorApplication(e, Translate(e.Head), Translate_definite_paren(e.Args[0])));
                    }

                case WKSID.integral:
                    Trace.Assert(e.Args.Length >= 2 && e.Args.Length <= 4);
                    return(__TranslateBigOp(e, e.Head, Unicode.I.INTEGRAL, e.Args.Length > 2 ? Translate(e.Args[2]) : null,
                                            e.Args.Length > 3 ? Translate(e.Args[3]) : null,
                                            __TranslateIntegralInternals(Translate(e.Args[0]), Translate(e.Args[1]))));

                case WKSID.log:
                    if (e.Args.Length == 2)
                    {
                        T f = __TranslateWord(wks, wks.ID.ToString(), Syntax.TType.LargeOp);
                        f = __AddSubscript(null, f, Translate(e.Args[0]));
                        f = ParenAround(e.Head, false, f);
                        return(__TranslateFunctionApplication(e, f, Translate_maybe_parenfn(e.Args[1], _showInvisibles)));
                    }
                    break;

                case WKSID.floor:
                    if (e.Args.Length == 1)
                    {
                        return(__TranslateDelims(e, false, Unicode.L.LEFT_FLOOR.ToString(), Unicode.L.LEFT_FLOOR,
                                                 __WrapTranslatedExpr(null, Translate(e.Args[0])),
                                                 Unicode.R.RIGHT_FLOOR.ToString(), Unicode.R.RIGHT_FLOOR));
                    }
                    break;

                case WKSID.ceiling:
                    if (e.Args.Length == 1)
                    {
                        return(__TranslateDelims(e, false, Unicode.L.LEFT_CEILING.ToString(), Unicode.L.LEFT_CEILING,
                                                 __WrapTranslatedExpr(null, Translate(e.Args[0])),
                                                 Unicode.R.RIGHT_CEILING.ToString(), Unicode.R.RIGHT_CEILING));
                    }
                    break;
                }
            }
            else if (e.Head == new LetterSym('→') || e.Head == new LetterSym(Unicode.R.RIGHTWARDS_DOUBLE_ARROW))
            {
                T arrow = __TranslateOperator(e.Head, ((LetterSym)e.Head).Letter, Syntax.TType.Rel);
                if (e.Args.Length == 1)
                {
                    return(__WrapTranslatedExpr(e, Translate(e.Args[0]), arrow));
                }
                else
                {
                    Trace.Assert(e.Args.Length == 2);
                    return(__WrapTranslatedExpr(e, Translate(e.Args[0]), arrow, Translate(e.Args[1])));
                }
            }
            else if (e.Head == new WordSym("brace"))
            {
                return(__TranslateDelims(e, false, "{", '{', __WrapTranslatedExpr(null, Translate_arglist(e, e.Args)), "}", '}'));
            }
            else if (e.Head == new WordSym("bracket"))
            {
                return(__TranslateDelims(e, false, "[", '[', __WrapTranslatedExpr(null, Translate_arglist(e, e.Args)), "]", ']'));
            }
            // FIXME: probably both branches here should use la and ra...
            if (e.Args.Length == 1)
            {
                if (e.Head is WellKnownSym)
                {
                    return(__TranslateFunctionApplication(e, Translate(e.Head), Translate_maybe_parenfn(e.Args[0], _showInvisibles)));
                }
                else
                {
                    return(__TranslateFunctionApplication(e, Translate_maybe_parenfn(e.Head), Translate_definite_paren(e.Args[0], _showInvisibles)));
                }
            }
            else
            {
                return(__TranslateFunctionApplication(e, Translate_maybe_parenfn(e.Head),
                                                      __TranslateDelims(e, _showInvisibles, "la", '(', __WrapTranslatedExpr(null, Translate_arglist(e, e.Args)), "ra", ')')));
            }
        }
Exemple #11
0
 protected override Box __TranslateOperator(Expr expr, object exprix, Syntax.WOrC op, Syntax.TType type)
 {
     if (op.Word == null)
     {
         return(new CharBox(expr, exprix, op.Character, type));
     }
     else
     {
         Trace.Assert(exprix == null);
         return(new StringBox(expr, op.Word, type));
     }
 }
Exemple #12
0
            private void ConsiderPairSpacing()
            {
                if (A == null || B == null)
                {
                    return;
                }
                Box a = A.Final.Target;
                Box b = B.Final.Target;

                /* Do TeX atom type and spacing fixup rules from p 170 and appendix G */
                Syntax.TType atype = GetTType(a), btype = GetTType(b);
                /* rule 20 */
                int spacing = IBSpacing[(int)atype, (int)btype];

                Trace.Assert(spacing != 5);
                Box space = null;

                switch (spacing)
                {
                case 0:
                    // all the code for this case is *not* part of TeX's rule 20. It comes from p 169 and applies specifically to factorial
                    // operators, so won't be correct for other possible uses of '!'
                    if (atype == Syntax.TType.Ord && a is CharBox && ((CharBox)a).C == '!')
                    {
                        CharBox   cb = b as CharBox;
                        StringBox sb = b as StringBox;
                        AtomBox   ab = b as AtomBox;
                        if ((btype == Syntax.TType.Ord && ((cb != null && (Char.IsLetter(cb.C) || Char.IsDigit(cb.C))) ||
                                                           (sb != null && (Char.IsLetter(sb.S, 0) || Char.IsDigit(sb.S, 0))))) ||
                            btype == Syntax.TType.Open)
                        {
                            space = ThinSkip();
                        }
                        else if (btype == Syntax.TType.Ord && ab != null)
                        {
                            cb = ab.Nucleus as CharBox;
                            sb = ab.Nucleus as StringBox;
                            if ((cb != null && (Char.IsLetter(cb.C) || Char.IsDigit(cb.C))) ||
                                (sb != null && (Char.IsLetter(sb.S, 0) || Char.IsDigit(sb.S, 0))))
                            {
                                space = ThinSkip();
                            }
                        }
                    }
                    break;

                case 1:
                    space = ThinSkip();
                    break;

                case 2:
                    space = MedSkip();
                    break;

                case 3:
                    space = ThickSkip();
                    break;

                case -1:
                    space = DTThinSkip();
                    break;

                case -2:
                    space = DTMedSkip();
                    break;

                case -3:
                    space = DTThickSkip();
                    break;
                }

                /* apply the spacing */
                if (space != null)
                {
                    /* Find the lowest common ancestor */
                    int found = -1;
                    for (int i = 0; i < A.P.Count && i < B.P.Count; i++)
                    {
                        if (A.P[i].Target != B.P[i].Target)
                        {
                            found = i;
                            break;
                        }
                    }
                    Trace.Assert(found != -1, "adjacent boxes are the same?");
                    Trace.Assert(A.P[found].B == B.P[found].B);
                    Trace.Assert(A.P[found].GetType() == B.P[found].GetType());
                    List <Box> boxes;
                    int        ix;
                    if (A.P[found] is HBoxLink)
                    {
                        HBoxLink hba = (HBoxLink)A.P[found];
                        HBoxLink hbb = (HBoxLink)B.P[found];
                        Trace.Assert(hba.I < hbb.I);
                        for (int i = hba.I + 1; i < hbb.I; i++)
                        {
                            Trace.Assert(hba.HB.Boxes[i] is MuSkipBox || hba.HB.Boxes[i] is DTMuSkipBox);
                        }
                        boxes = hba.HB.Boxes;
                        ix    = hbb.I;
                    }
                    else
                    {
                        DelimitedBoxLink dba = (DelimitedBoxLink)A.P[found];
                        DelimitedBoxLink dbb = (DelimitedBoxLink)B.P[found];
                        boxes = dba.DB.Contents.Boxes;
                        if (dba.Piece == DelimitedBoxLink.Which.Left)
                        {
                            Trace.Assert(dbb.Piece != DelimitedBoxLink.Which.Left);
                            ix = 0;
                        }
                        else
                        {
                            Trace.Assert(dba.Piece == DelimitedBoxLink.Which.Contents);
                            Trace.Assert(dbb.Piece == DelimitedBoxLink.Which.Right);
                            ix = boxes.Count;
                        }
                    }
                    SortedDictionary <int, Box> inserts;
                    if (!_listBoxInserts.TryGetValue(boxes, out inserts))
                    {
                        inserts = new SortedDictionary <int, Box>();
                        _listBoxInserts[boxes] = inserts;
                    }
                    inserts.Add(ix, space);
                }
            }
Exemple #13
0
            private void ConsiderPairTType()
            {
                Box a = A == null ? null : A.P.Count == 0 ? _toplevel : A.Final.Target;
                Box b = B == null ? null : B.P.Count == 0 ? _toplevel : B.Final.Target;

                /* Do TeX atom type rules from p 170 and appendix G */
                Syntax.TType atype = GetTType(a), btype = GetTType(b);

                /* appendix G, p 442- */
                /* rule 1, 2, 3, 4: nothing for us */
                /* rule 5 */
                if (btype == Syntax.TType.Op)
                {
                    if (a == null || atype == Syntax.TType.Op || atype == Syntax.TType.LargeOp || atype == Syntax.TType.Rel || atype == Syntax.TType.Open ||
                        atype == Syntax.TType.Punct)
                    {
                        btype = SetTType(b, Syntax.TType.Ord);
                        goto rule14;
                    }
                    else
                    {
                        goto rule17;
                    }
                }
                /* rule 6 */
                if ((btype == Syntax.TType.Rel || btype == Syntax.TType.Close || btype == Syntax.TType.Punct) && atype == Syntax.TType.Op)
                {
                    atype = SetTType(a, Syntax.TType.Ord);
                    goto rule17;
                }
                /* rule 7 */
                if (btype == Syntax.TType.Open || btype == Syntax.TType.Inner)
                {
                    goto rule17;
                }
                /* rule 8: (Vcent) done in type inference above */
                /* rule 9: change Over to Ord and go to 17: we have no Over use; if we do, just consider it Ord to start with above */
                /* rule 10: change Under to Ord and go to 17: we have no Under use; if we do, just consider it Ord to start with above */
                /* rule 11: (Rad) done in type inference above */
                /* rule 12: change Acc to Ord and go to 17: we have no Acc use; if we do, just consider it Ord to start with above */
                /* rule 13, 13a (Op) */
                /* limits handling done in AtomBox */
                // FIXME: should make operators like integral and summation slightly larger in display style according to TeXbook
                /* rule 14: (Ord) ligatures and kerns here too; we ignore for now */
rule14:
                // FIXME should do ligatures and kerns?
                if (btype == Syntax.TType.Ord)
                {
                    goto rule17;
                }
                /* rule 15, 15a-e: generalized fraction (before becoming Inner): nothing for us (should have been done in AtopBox) */
                // Not clear if things matching rule15 should fall through, as makes little sense to change type to Inner right before changing to Ord
                /* rule 16: change current type to Ord: folded in to previous jumps to rule16 including changing to be jump to rule 17 */
                /* rule 17: various things which are either done elsewhere or which we don't handle */
rule17:
                /* rule 18, 18a-f: done in AtomBox */
                ;
                /* rule (unnumbered, between 18f and 19) */
                if (atype == Syntax.TType.Op && btype == Syntax.TType.None)
                {
                    SetTType(a, Syntax.TType.Ord);
                }
            }