/* ** Small-C Compiler -- Part 3 -- Expression Analyzer. ** Copyright 1982, 1983, 1985, 1988 J. E. Hendrix ** Copyright 1998 H T Walheim ** All rights reserved. */ #include <stdio.h> #include "cc.h" #define ST 0 /* is[ST] - symbol table address, else 0 */ #define TI 1 /* is[TI] - type of indirect obj to fetch, else 0 */ #define TA 2 /* is[TA] - type of address, else 0 */ #define TC 3 /* is[TC] - type of constant (INT or UINT), else 0 */ #define CV 4 /* is[CV] - value of constant (+ auxiliary uses) */ #define OP 5 /* is[OP] - code of highest/last binary operator */ #define SA 6 /* is[SA] - stage address of "op 0" code, else 0 */ extern char *litq, *glbptr, *lptr, ssname[NAMESIZE], quote[2]; extern int ch, csp, litlab, litptr, nch, op[16], op2[16], opindex, opsize, *snext; /***************** lead-in functions *******************/ constexpr(val) int *val; { int constant; int *before, *start; setstage(&before, &start); expression(&constant, val); clearstage(before, 0); /* scratch generated code */ if(constant == 0) error("must be constant expression"); return constant; } null_is (is) int is[7]; { int i; for (i = 0; i < 7; ++i) { is[i] = 0; /* <-- */ } } expression(con, val) int *con, *val; { int i; int is[7]; null_is (is); if(level1(is)) fetch(is); *con = is[TC]; *val = is[CV]; } test(label, parens) int label, parens; { int is[7]; int *before, *start; null_is (is); if(parens) need("("); while(1) { setstage(&before, &start); if(level1(is)) fetch(is); if(match(",")) clearstage(before, start); else break; } if(parens) need(")"); if(is[TC]) { /* constant expression */ clearstage(before, 0); if(is[CV]) return; gen(JMPm, label); return; } if(is[SA]) { /* stage address of "oper 0" code */ switch(is[OP]) { /* operator code */ case EQ12: case LE12u: zerojump(EQ10f, label, is); break; case NE12: case GT12u: zerojump(NE10f, label, is); break; case GT12: zerojump(GT10f, label, is); break; case GE12: zerojump(GE10f, label, is); break; case GE12u: clearstage(is[SA], 0); break; case LT12: zerojump(LT10f, label, is); break; case LT12u: zerojump(JMPm, label, is); break; case LE12: zerojump(LE10f, label, is); break; default: gen(NE10f, label); break; } } else gen(NE10f, label); clearstage(before, start); } /* ** test primary register against zero and jump if false */ zerojump(oper, label, is) int oper, label, is[]; { clearstage(is[SA], 0); /* purge conventional code */ gen(oper, label); } /***************** precedence levels ******************/ #ifdef _MSC_VER level2(); level3(); level4(); level5(); level6(); level7(); level8(); level9(); level10(); level11(); level12(); level13(); #endif level1(is) int is[]; { int k, is2[7], is3[2], oper, oper2; null_is (is2); null_is (is3); k = down1(level2, is); if(is[TC]) { #ifdef INT32 gen(GETd1n, is[CV]); #else gen(GETw1n, is[CV]); #endif } if(match("|=")) {oper = oper2 = OR12;} else if(match("^=")) {oper = oper2 = XOR12;} else if(match("&=")) {oper = oper2 = AND12;} else if(match("+=")) {oper = oper2 = ADD12;} else if(match("-=")) {oper = oper2 = SUB12;} else if(match("*=")) {oper = MUL12; oper2 = MUL12u;} else if(match("/=")) {oper = DIV12; oper2 = DIV12u;} else if(match("%=")) {oper = MOD12; oper2 = MOD12u;} else if(match(">>=")) {oper = oper2 = ASR12;} else if(match("<<=")) {oper = oper2 = ASL12;} else if(match("=")) {oper = oper2 = 0;} else return k; /* have an assignment operator */ if(k == 0) { needlval(); return 0; } is3[ST] = is[ST]; is3[TI] = is[TI]; if(is[TI]) { /* indirect target */ if(oper) { /* ?= */ gen(PUSH1, 0); /* save address */ fetch(is); /* fetch left side */ } down2(oper, oper2, level1, is, is2); /* parse right side */ if(oper) gen(POP2, 0); /* retrieve address */ } else { /* direct target */ if(oper) { /* ?= */ fetch(is); /* fetch left side */ down2(oper, oper2, level1, is, is2); /* parse right side */ } else { /* = */ if(level1(is2)) fetch(is2); /* parse right side */ } } store(is3); /* store result */ return 0; } level2(is1) int is1[]; { int is2[7], is3[7], k, flab, endlab, *before, *after; null_is (is2); null_is (is3); k = down1(level3, is1); /* expression 1 */ if(match("?") == 0) return k; dropout(k, NE10f, flab = getlabel(), is1); if(down1(level2, is2)) fetch(is2); /* expression 2 */ else if(is2[TC]) { #ifdef INT32 gen(GETd1n, is2[CV]); #else gen(GETw1n, is2[CV]); #endif } need(":"); gen(JMPm, endlab = getlabel()); gen(LABm, flab); if(down1(level2, is3)) fetch(is3); /* expression 3 */ else if(is3[TC]) { #ifdef INT32 gen(GETd1n, is3[CV]); #else gen(GETw1n, is3[CV]); #endif } gen(LABm, endlab); is1[TC] = is1[CV] = 0; if(is2[TC] && is3[TC]) { /* expr1 ? const2 : const3 */ is1[TA] = is1[TI] = is1[SA] = 0; } else if(is3[TC]) { /* expr1 ? var2 : const3 */ is1[TA] = is2[TA]; is1[TI] = is2[TI]; is1[SA] = is2[SA]; } else if((is2[TC]) /* expr1 ? const2 : var3 */ || (is2[TA] == is3[TA])) { /* expr1 ? same2 : same3 */ is1[TA] = is3[TA]; is1[TI] = is3[TI]; is1[SA] = is3[SA]; } else error("mismatched expressions"); return 0; } level3 (is) int is[]; {return skim("||", EQ10f, 1, 0, level4, is);} level4 (is) int is[]; {return skim("&&", NE10f, 0, 1, level5, is);} level5 (is) int is[]; {return down("|", 0, level6, is);} level6 (is) int is[]; {return down("^", 1, level7, is);} level7 (is) int is[]; {return down("&", 2, level8, is);} level8 (is) int is[]; {return down("== !=", 3, level9, is);} level9 (is) int is[]; {return down("<= >= < >", 5, level10, is);} level10(is) int is[]; {return down(">> <<", 9, level11, is);} level11(is) int is[]; {return down("+ -", 11, level12, is);} level12(is) int is[]; {return down("* / %", 13, level13, is);} level13(is) int is[]; { int k; char *ptr; if(match("++")) { /* ++lval */ if(level13(is) == 0) { needlval(); return 0; } step(rINC1, is, 0); return 0; } else if(match("--")) { /* --lval */ if(level13(is) == 0) { needlval(); return 0; } step(rDEC1, is, 0); return 0; } else if(match("~")) { /* ~ */ if(level13(is)) fetch(is); gen(COM1, 0); is[CV] = ~ is[CV]; return (is[SA] = 0); } else if(match("!")) { /* ! */ if(level13(is)) fetch(is); gen(LNEG1, 0); is[CV] = ! is[CV]; return (is[SA] = 0); } else if(match("-")) { /* unary - */ if(level13(is)) fetch(is); gen(ANEG1, 0); is[CV] = -is[CV]; return (is[SA] = 0); } else if(match("*")) { /* unary * */ if(level13(is)) fetch(is); if(ptr = is[ST]) is[TI] = ptr[TYPE]; else is[TI] = INT; is[SA] = /* no (op 0) stage address */ is[TA] = /* not an address */ is[TC] = 0; /* not a constant */ is[CV] = 1; /* omit fetch() on func call */ return 1; } else if(amatch("sizeof", 6)) { /* sizeof() */ int sz, p; char *ptr, sname[NAMESIZE]; if(match("(")) p = 1; else p = 0; sz = 0; if (amatch("unsigned", 8)) sz = INTSIZE; if (amatch("int", 3)) sz = INTSIZE; else if(amatch("char", 4)) sz = 1; if(sz) {if(match("*")) sz = PTRSIZE;} else if(symname(sname) && ((ptr = findloc(sname)) || (ptr = findglb(sname))) && ptr[IDENT] != FUNCTION && ptr[IDENT] != LABEL) sz = getint(ptr+SIZE, INTSIZE); else if(sz == 0) error("must be object or type"); if(p) need(")"); is[TC] = INT; is[CV] = sz; is[TA] = is[TI] = is[ST] = 0; return 0; } else if(match("&")) { /* unary & */ if(level13(is) == 0) { error("illegal address"); return 0; } ptr = is[ST]; is[TA] = ptr[TYPE]; if(is[TI]) return 0; gen(POINT1m, ptr); is[TI] = ptr[TYPE]; return 0; } else { k = level14(is); if(match("++")) { /* lval++ */ if(k == 0) { needlval(); return 0; } step(rINC1, is, rDEC1); return 0; } else if(match("--")) { /* lval-- */ if(k == 0) { needlval(); return 0; } step(rDEC1, is, rINC1); return 0; } else return k; } } level14(is) int *is; { int k, consta, val; char *ptr, *before, *start; k = primary(is); ptr = is[ST]; blanks(); if(ch == '[' || ch == '(') { int is2[7]; /* allocate only if needed */ null_is (is2); while(1) { if(match("[")) { /* [subscript] */ if(ptr == 0) { error("can't subscript"); skip(); need("]"); return 0; } if(is[TA]) {if(k) fetch(is);} else {error("can't subscript"); k = 0;} setstage(&before, &start); is2[TC] = 0; down2(0, 0, level1, is2, is2); need("]"); if(is2[TC]) { clearstage(before, 0); if(is2[CV]) { /* only add if non-zero */ if(ptr[TYPE] >> 2 == BPD) { #ifdef INT32 gen(GETd2n, is2[CV] << LBPD); #else gen(GETw2n, is2[CV] << LBPD); #endif } else if(ptr[TYPE] >> 2 == BPW) { #ifdef INT32 gen(GETd2n, is2[CV] << LBPW); #else gen(GETw2n, is2[CV] << LBPW); #endif } else { #ifdef INT32 gen(GETd2n, is2[CV]); #else gen(GETw2n, is2[CV]); #endif } gen(ADD12, 0); } } else { if(ptr[TYPE] >> 2 == BPD) { gen(DBL1, 0); gen(DBL1, 0); } else if(ptr[TYPE] >> 2 == BPW) { gen(DBL1, 0); } gen(ADD12, 0); } is[TA] = 0; is[TI] = ptr[TYPE]; k = 1; } else if(match("(")) { /* function(...) */ if(ptr == 0) callfunc(0); else if(ptr[IDENT] != FUNCTION) { if(k && !is[CV]) fetch(is); callfunc(0); } else callfunc(ptr); k = is[ST] = is[TC] = is[CV] = 0; } else return k; } } if(ptr && ptr[IDENT] == FUNCTION) { gen(POINT1m, ptr); is[ST] = 0; return 0; } return k; } primary(is) int *is; { char *ptr, sname[NAMESIZE]; int k; if(match("(")) { /* (subexpression) */ do k = level1(is); while(match(",")); need(")"); return k; } putint(0, is, 7 << LBPW); /* clear "is" array */ if(symname(sname)) { /* is legal symbol */ if(ptr = findloc(sname)) { /* is local */ if(ptr[IDENT] == LABEL) { experr(); return 0; } gen(POINT1s, getint(ptr+OFFSET, INTSIZE)); is[ST] = ptr; is[TI] = ptr[TYPE]; if(ptr[IDENT] == ARRAY) { is[TA] = ptr[TYPE]; return 0; } if(ptr[IDENT] == POINTER) { is[TI] = UINT; is[TA] = ptr[TYPE]; } return 1; } if(ptr = findglb(sname)) { /* is global */ is[ST] = ptr; if(ptr[IDENT] != FUNCTION) { if(ptr[IDENT] == ARRAY) { gen(POINT1m, ptr); is[TI] = is[TA] = ptr[TYPE]; return 0; } if(ptr[IDENT] == POINTER) is[TA] = ptr[TYPE]; return 1; } } else is[ST] = addsym(sname, FUNCTION, INT, 0, 0, &glbptr, AUTOEXT); return 0; } if(constant(is) == 0) experr(); return 0; } experr() { error("invalid expression"); #ifdef INT32 gen(GETd1n, 0); #else gen(GETw1n, 0); #endif skip(); } /* attempt at right to left - do it later */ #ifdef LATER pushargs (ptr, nargs) char *ptr; int nargs; { if (streq(lptr, ")") != 0) return; if(endst()) return; if(ptr) { expression(&consta, &val); gen(PUSH1, 0); } else { gen(PUSH1, 0); expression(&consta, &val); gen(SWAP1s, 0); /* don't push addr */ } nargs = nargs + INTSIZE; /* count args*BPW */ if(match(",") == 0) break; } #endif callfunc(ptr) char *ptr; /* symbol table entry or 0 */ { int nargs, consta, val; nargs = 0; blanks(); /* already saw open paren */ while(streq(lptr, ")") == 0) { if(endst()) break; if(ptr) { expression(&consta, &val); gen(PUSH1, 0); } else { gen(PUSH1, 0); expression(&consta, &val); gen(SWAP1s, 0); /* don't push addr */ } nargs = nargs + INTSIZE; /* count args*BPW */ if(match(",") == 0) break; } need(")"); if(ptr && (streq(ptr + NAME, "CCARGC") == 0)) /* accessing ... like va_args */ #ifdef INT32 gen(ARGCNTn, nargs >> LBPD); /* to get start of frame */ #else gen(ARGCNTn, nargs >> LBPW); /* to get start of frame */ #endif if(ptr) gen(CALLm, ptr); else gen(CALL1, 0); gen(ADDSP, csp + nargs); } /* ** true if is2's operand should be doubled */ fdouble (oper, is1, is2) int oper, is1[], is2[]; { ////////////////////////////////////////////////////// // This is a 'magic' function, its usage and function // are not so obvious // // Purpose: when indexing an address we must know // what we are pointing at so that the indexsize is // proper, e,g, // charptr++, should multiply index by a 1 // shortptr++, should multiply index by a 2 // intptr++, should multiply index by a 4 // // Algorithm: // IF // operation is ADD12 or SUB12 // AND // is1 is an address (pointer, array or via & operator // AND // is2 is NOT an address (pointer, array or via & operator // THEN // return the multiplication factor based on s1 (or true) // ELSE // return 0 (or false) // // Usage: The return value is used as a 'boolean' // for nonconstant values, indicating that runtime code // is necessary to do the necessary multiplication // For contant value the return value is used to do a compile-time // multiplication (shift actually) // if ((oper == ADD12 || oper == SUB12) && (is2[TA] == 0)) { switch (is1[TA]>>2) { default: case 1: // char return (0); case 2: // short return (1); case 4: // int return (2); } } return (0); /* - original code - if((oper != ADD12 && oper != SUB12) || (is1[TA] >> 2 != BPW) || (is2[TA])) return 0; return 1; */ } step (oper, is, oper2) int oper, is[], oper2; { fetch(is); gen(oper, is[TA] ? (is[TA] >> 2) : 1); store(is); if(oper2) gen(oper2, is[TA] ? (is[TA] >> 2) : 1); } store(is) int is[]; { char *ptr; if(is[TI]) { /* putstk */ if(is[TI] >> 2 == 1) { gen(PUTbp1, 0); } else if(is[TI] >> 2 == 2) { gen(PUTwp1, 0); } else { gen(PUTdp1, 0); } } else { /* putmem */ ptr = is[ST]; if(ptr[IDENT] == POINTER) { #ifdef INT32 /* int and ptr-size are ALWAYS the same */ gen(PUTdm1, ptr); #else gen(PUTwm1, ptr); #endif } else if (ptr[TYPE] >> 2 == 1) { gen(PUTbm1, ptr); } else if (ptr[TYPE] >> 2 == BPW) { gen(PUTwm1, ptr); } else { gen(PUTdm1, ptr); } } } fetch(is) int is[]; { char *ptr; ptr = is[ST]; if(is[TI]) /* indirect */ { if(is[TI] >> 2 == BPD) /* pointer to DWORD */ { gen(GETd1p, 0); } else if(is[TI] >> 2 == BPW) /* pointer to WORD */ { /* if INT32 must make distinction between signed/unsigned <-- */ gen(GETw1p, 0); } else { if(ptr[TYPE] & UNSIGNED) gen(GETb1pu, 0); else gen(GETb1p, 0); } } else { /* direct */ if(ptr[IDENT] == POINTER) { #ifdef INT32 gen(GETd1m, ptr); #else gen(GETw1m, ptr); #endif } else if (ptr[TYPE] >> 2 == BPD) { gen(GETd1m, ptr); } else if (ptr[TYPE] >> 2 == BPW) { gen(GETw1m, ptr); } else { if(ptr[TYPE] & UNSIGNED) gen(GETb1mu, ptr); else gen(GETb1m, ptr); } } } constant(is) int is[]; { int offset; if (is[TC] = number(is + CV)) { #ifdef INT32 gen(GETd1n, is[CV]); #else gen(GETw1n, is[CV]); #endif } else if(is[TC] = chrcon(is + CV)) { #ifdef INT32 gen(GETd1n, is[CV]); #else gen(GETw1n, is[CV]); #endif } else if(string(&offset)) { gen(POINT1l, offset); } else { return 0; } return 1; } number(value) int *value; { int k, minus; k = minus = 0; while(1) { if(match("+")) ; else if(match("-")) minus = 1; else break; } if(isdigit(ch) == 0) return 0; if(ch == '0') { while(ch == '0') inbyte(); if(toupper(ch) == 'X') { inbyte(); while(isxdigit(ch)) { if(isdigit(ch)) k = k*16 + (inbyte() - '0'); else k = k*16 + 10 + (toupper(inbyte()) - 'A'); } } else while (ch >= '0' && ch <= '7') k = k*8 + (inbyte() - '0'); } else while (isdigit(ch)) k = k*10 + (inbyte() - '0'); if(minus) { *value = -k; return (INT); } if((*value = k) < 0) return (UINT); else return (INT); } chrcon(value) int *value; { int k; k = 0; if(match("'") == 0) return 0; while(ch != '\'') k = (k << 8) + (litchar() & 255); gch(); *value = k; return (INT); } string(offset) int *offset; { char c; if(match(quote) == 0) return 0; *offset = litptr; while (ch != '"') { if(ch == 0) break; stowlit(litchar(), 1); } gch(); litq[litptr++] = 0; return 1; } stowlit(value, size) int value, size; { if((litptr+size) >= LITMAX) { error("literal queue overflow"); exit(ERRCODE); } putint(value, litq+litptr, size); litptr += size; } litchar() { int i, oct; if(ch != '\\' || nch == 0) return gch(); gch(); switch(ch) { case 'n': gch(); return NEWLINE; case 't': gch(); return 9; /* HT */ case 'b': gch(); return 8; /* BS */ case 'f': gch(); return 12; /* FF */ } i = 3; oct = 0; while((i--) > 0 && ch >= '0' && ch <= '7') oct = (oct << 3) + gch() - '0'; if(i == 2) return gch(); else return oct; } /***************** pipeline functions ******************/ /* ** skim over terms adjoining || and && operators */ skim(opstr, tcode, dropval, endval, level, is) char *opstr; int tcode, dropval, endval, (*level)(), is[]; { int k, droplab, endlab; droplab = 0; while(1) { k = down1(level, is); if(nextop(opstr)) { bump(opsize); if(droplab == 0) droplab = getlabel(); dropout(k, tcode, droplab, is); } else if(droplab) { dropout(k, tcode, droplab, is); #ifdef INT32 gen(GETd1n, endval); #else gen(GETw1n, endval); #endif gen(JMPm, endlab = getlabel()); gen(LABm, droplab); #ifdef INT32 gen(GETd1n, dropval); #else gen(GETw1n, dropval); #endif gen(LABm, endlab); is[TI] = is[TA] = is[TC] = is[CV] = is[SA] = 0; return 0; } else return k; } } /* ** test for early dropout from || or && sequences */ dropout(k, tcode, exit1, is) int k, tcode, exit1, is[]; { if(k) fetch(is); else if(is[TC]) { #ifdef INT32 gen(GETd1n, is[CV]); #else gen(GETw1n, is[CV]); #endif } gen(tcode, exit1); /* jumps on false */ } /* ** drop to a lower level */ down(opstr, opoff, level, is) char *opstr; int opoff, (*level)(), is[]; { int k; k = down1(level, is); if(nextop(opstr) == 0) return k; if(k) fetch(is); while(1) { if(nextop(opstr)) { int is2[7]; /* allocate only if needed */ null_is (is2); bump(opsize); opindex += opoff; down2(op[opindex], op2[opindex], level, is, is2); } else return 0; } } /* ** unary drop to a lower level */ down1(level, is) int (*level)(), is[]; { int k, *before, *start; setstage(&before, &start); k = (*level)(is); if(is[TC]) clearstage(before, 0); /* load constant later */ return k; } /* ** binary drop to a lower level */ down2(oper, oper2, level, is, is2) int oper, oper2, (*level)(), is[], is2[]; { int *before, *start; char *ptr; int value; setstage(&before, &start); is[SA] = 0; /* not "... op 0" syntax */ if(is[TC]) { /* consant op unknown */ if(down1(level, is2)) fetch(is2); if(is[CV] == 0) is[SA] = snext; gen(GETw2n, is[CV] << fdouble(oper, is2, is)); } else { /* variable op unknown */ gen(PUSH1, 0); /* at start in the buffer */ if(down1(level, is2)) fetch(is2); if(is2[TC]) { /* variable op constant */ if(is2[CV] == 0) is[SA] = start; #ifdef INT32 csp += BPD; /* adjust stack and */ #else csp += BPW; /* adjust stack and */ #endif clearstage(before, 0); /* discard the PUSH */ if(oper == ADD12) { /* commutative */ #ifdef INT32 gen(GETd2n, is2[CV] << fdouble(oper, is, is2)); #else gen(GETw2n, is2[CV] << fdouble(oper, is, is2)); #endif } else { /* non-commutative */ gen(MOVE21, 0); #ifdef INT32 gen(GETd1n, is2[CV] << fdouble(oper, is, is2)); #else gen(GETw1n, is2[CV] << fdouble(oper, is, is2)); #endif } } else { /* variable op variable */ gen(POP2, 0); if(value = fdouble(oper, is, is2)) { gen(DBL1, 0); // index size 2 if (value > 1) gen(DBL1, 0); // .. or even 4 } if(value = fdouble(oper, is2, is)) { gen(DBL2, 0); if (value > 1) gen(DBL2, 0); } } } if(oper) { if(nosign(is) || nosign(is2)) oper = oper2; if(is[TC] = is[TC] & is2[TC]) { /* constant result */ is[CV] = calc(is[CV], oper, is2[CV]); clearstage(before, 0); if(is2[TC] == UINT) is[TC] = UINT; } else { /* variable result */ gen(oper, 0); if(oper == SUB12 && is [TA] >> 2 == BPW && is2[TA] >> 2 == BPW) { /* difference of two word addresses */ gen(SWAP12, 0); gen(GETw1n, 1); gen(ASR12, 0); /* div by 2 */ } is[OP] = oper; /* identify the operator */ } if(oper == SUB12 || oper == ADD12) { if(is[TA] && is2[TA]) /* addr +/- addr */ is[TA] = 0; else if(is2[TA]) { /* value +/- addr */ is[ST] = is2[ST]; is[TI] = is2[TI]; is[TA] = is2[TA]; } } if(is[ST] == 0 || ((ptr = is2[ST]) && (ptr[TYPE] & UNSIGNED))) is[ST] = is2[ST]; } } /* ** unsigned operand? */ nosign(is) int is[]; { char *ptr; if(is[TA] || is[TC] == UINT || ((ptr = is[ST]) && (ptr[TYPE] & UNSIGNED)) ) return 1; return 0; } /* ** calcualte signed constant result */ calc(left, oper, right) int left, oper, right; { switch(oper) { case ADD12: return (left + right); case SUB12: return (left - right); case MUL12: return (left * right); case DIV12: return (left / right); case MOD12: return (left % right); case EQ12: return (left == right); case NE12: return (left != right); case LE12: return (left <= right); case GE12: return (left >= right); case LT12: return (left < right); case GT12: return (left > right); case AND12: return (left & right); case OR12: return (left | right); case XOR12: return (left ^ right); case ASR12: return (left >> right); case ASL12: return (left << right); } return (calc2(left, oper, right)); } /* ** calcualte unsigned constant result */ calc2(left, oper, right) unsigned left, right; int oper; { switch(oper) { case MUL12u: return (left * right); case DIV12u: return (left / right); case MOD12u: return (left % right); case LE12u: return (left <= right); case GE12u: return (left >= right); case LT12u: return (left < right); case GT12u: return (left > right); } return (0); }