1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-03 07:24:58 +03:00
inform7/inter/building-module/Chapter 2/Inform 6 Operators.w

188 lines
7.4 KiB
OpenEdge ABL

[I6Operators::] Inform 6 Operators.
Order of precedence for Inform 6 operators, regarded as their Inter primitive
equivalents.
@ The following table of data is essentially the same one in shown in section
6.2 of the Inform 6 Technical Manual: which operators take precedence over
which others, which are right or left associative, which are prefix or postfix,
and so on.
=
typedef struct i6_operator_metadata {
inter_ti BIP; /* which may be a |*_XBIP| value */
int precedence; /* operators with higher precedence bind more tightly */
int prefix; /* |TRUE| for prefix, |FALSE| for suffix, |NOT_APPLICABLE| for infix */
int right_associative; /* if same operator used twice at same precedence */
int arity; /* how many operands the operator has: always 1 or 2 */
char *notation_c; /* Inform 6 syntax for this operator */
struct text_stream *notation;
} i6_operator_metadata;
@ Do not reorder this table without reading //I6Operators::notation_to_BIP// below.
=
i6_operator_metadata i6_operator_chart[] = {
{ STORE_BIP, 1, NOT_APPLICABLE, FALSE, 2, "=", NULL },
{ AND_BIP, 2, NOT_APPLICABLE, TRUE, 2, "&&", NULL },
{ OR_BIP, 2, NOT_APPLICABLE, TRUE, 2, "||", NULL },
{ NOT_BIP, 2, TRUE, TRUE, 1, "~~", NULL },
{ EQ_BIP, 3, NOT_APPLICABLE, TRUE, 2, "==", NULL },
{ GT_BIP, 3, NOT_APPLICABLE, TRUE, 2, ">", NULL },
{ GE_BIP, 3, NOT_APPLICABLE, TRUE, 2, ">=", NULL },
{ LT_BIP, 3, NOT_APPLICABLE, TRUE, 2, "<", NULL },
{ LE_BIP, 3, NOT_APPLICABLE, TRUE, 2, "<=", NULL },
{ NE_BIP, 3, NOT_APPLICABLE, TRUE, 2, "~=", NULL },
{ HAS_XBIP, 3, NOT_APPLICABLE, TRUE, 2, "has", NULL },
{ HASNT_XBIP, 3, NOT_APPLICABLE, TRUE, 2, "hasnt", NULL },
{ OFCLASS_BIP, 3, NOT_APPLICABLE, TRUE, 2, "ofclass", NULL },
{ PROPERTYEXISTS_BIP, 3, NOT_APPLICABLE, TRUE, 2, "provides", NULL },
{ IN_BIP, 3, NOT_APPLICABLE, TRUE, 2, "in", NULL },
{ NOTIN_BIP, 3, NOT_APPLICABLE, TRUE, 2, "notin", NULL },
{ ALTERNATIVE_BIP, 4, NOT_APPLICABLE, TRUE, 2, "or", NULL },
{ ALTERNATIVECASE_BIP, 4, NOT_APPLICABLE, TRUE, 2, "", NULL },
{ PLUS_BIP, 5, NOT_APPLICABLE, TRUE, 2, "+", NULL },
{ MINUS_BIP, 5, NOT_APPLICABLE, TRUE, 2, "-", NULL },
{ TIMES_BIP, 6, NOT_APPLICABLE, TRUE, 2, "*", NULL },
{ DIVIDE_BIP, 6, NOT_APPLICABLE, TRUE, 2, "/", NULL },
{ MODULO_BIP, 6, NOT_APPLICABLE, TRUE, 2, "%", NULL },
{ BITWISEAND_BIP, 6, NOT_APPLICABLE, TRUE, 2, "&", NULL },
{ BITWISEOR_BIP, 6, NOT_APPLICABLE, TRUE, 2, "|", NULL },
{ BITWISENOT_BIP, 6, TRUE, TRUE, 1, "~", NULL },
{ LOOKUP_BIP, 7, NOT_APPLICABLE, TRUE, 2, "-->", NULL },
{ LOOKUPBYTE_BIP, 7, NOT_APPLICABLE, TRUE, 2, "->", NULL },
{ UNARYMINUS_BIP, 8, NOT_APPLICABLE, TRUE, 1, "-", NULL },
{ POSTINCREMENT_BIP, 9, FALSE, TRUE, 1, "++", NULL },
{ POSTDECREMENT_BIP, 9, FALSE, TRUE, 1, "--", NULL },
{ PREINCREMENT_BIP, 9, TRUE, TRUE, 1, "++", NULL },
{ PREDECREMENT_BIP, 9, TRUE, TRUE, 1, "--", NULL },
{ PROPERTYARRAY_BIP, 10, NOT_APPLICABLE, TRUE, 2, ".&", NULL },
{ PROPERTYLENGTH_BIP, 10, NOT_APPLICABLE, TRUE, 2, ".#", NULL },
{ PROPERTYVALUE_BIP, 12, NOT_APPLICABLE, TRUE, 2, ".", NULL },
{ OWNERKIND_XBIP, 13, NOT_APPLICABLE, TRUE, 2, ">>", NULL },
{ 0, -1, NOT_APPLICABLE, TRUE, 0, "", NULL }
};
@ The following must be called before the above array can be used. It checks
that the numbering is right, and converts the names and signatures from |char *|
to |text_stream *|.
=
int inform6_operators_chart_prepared = FALSE;
int inform6_operators_xref[MAX_BIPS];
int inform6_operators_XBIP_xref[MAX_BIPS];
void I6Operators::prepare_chart(void) {
if (inform6_operators_chart_prepared == FALSE) {
inform6_operators_chart_prepared = TRUE;
for (int i=0; i<MAX_BIPS; i++) inform6_operators_xref[i] = -1;
for (int i=0; i<MAX_BIPS; i++) inform6_operators_XBIP_xref[i] = -1;
for (inter_ti i=0; ; i++) {
if (i6_operator_chart[i].BIP == 0) break;
if (i >= MAX_BIPS) internal_error("MAX_BIPS set too low");
inter_ti BIP = i6_operator_chart[i].BIP;
if (BIP >= LOWEST_XBIP_VALUE) {
if (BIP > HIGHEST_XBIP_VALUE) internal_error("XBIP value out of range");
inform6_operators_XBIP_xref[BIP - LOWEST_XBIP_VALUE] = (int) i;
} else {
if (BIP >= MAX_BIPS) internal_error("BIP value out of range");
inform6_operators_xref[BIP] = (int) i;
}
i6_operator_chart[i].notation = Str::new();
WRITE_TO(i6_operator_chart[i].notation, "%s",
i6_operator_chart[i].notation_c);
}
}
}
@ This, again, is trickier than it looks because a valid input can be either a
|*_BIP| code or a |*_XBIP| code:
=
i6_operator_metadata *I6Operators::operator_for_BIP(inter_ti BIP) {
I6Operators::prepare_chart();
if ((BIP < MAX_BIPS) && (inform6_operators_xref[BIP] >= 0))
return &(i6_operator_chart[inform6_operators_xref[BIP]]);
if ((BIP >= LOWEST_XBIP_VALUE) && (BIP <= OWNERKIND_XBIP) &&
(inform6_operators_XBIP_xref[BIP - LOWEST_XBIP_VALUE] >= 0))
return &(i6_operator_chart[inform6_operators_XBIP_xref[BIP - LOWEST_XBIP_VALUE]]);
return NULL;
}
@ So now we have the outward-facing API. Here's the Inform 6 notation:
=
text_stream *I6Operators::I6_notation_for(inter_ti BIP) {
i6_operator_metadata *md = I6Operators::operator_for_BIP(BIP);
return (md) ? (md->notation) : I"???";
}
@ And the inverse of that function, from text to an operator, or 0 if none
matches. Note that we return the earliest row in the above table, if more than
one matches, and this matters for |++| and |--|, where the same notation is
used both for the prefix and postfix operators.
=
inter_ti I6Operators::notation_to_BIP(text_stream *T) {
I6Operators::prepare_chart();
for (inter_ti i=0; ; i++) {
if (i6_operator_chart[i].BIP == 0) break;
if (Str::eq(T, i6_operator_chart[i].notation)) return i6_operator_chart[i].BIP;
}
return 0;
}
@ The arity of the operator, always 1 or 2, unless the given |BIP| is not
an operator at all:
=
int I6Operators::arity(inter_ti BIP) {
i6_operator_metadata *md = I6Operators::operator_for_BIP(BIP);
return (md) ? (md->arity) : 0;
}
@ Returns:
(*) |TRUE| for a prefix operator, e.g., |++alpha|;
(*) |FALSE| for a postfix operator, e.g., |alpha--|;
(*) |NOT_APPLICABLE| for an infix operator, e.g., |alpha + beta|, or for
something which is not an operator at all.
=
int I6Operators::prefix(inter_ti BIP) {
i6_operator_metadata *md = I6Operators::operator_for_BIP(BIP);
return (md) ? (md->prefix) : NOT_APPLICABLE;
}
@ The precedence level of the operator, or infinity (near enough) if this is
not an operator at all.
@d UNPRECEDENTED_OPERATOR 10000
=
int I6Operators::precedence(inter_ti BIP) {
i6_operator_metadata *md = I6Operators::operator_for_BIP(BIP);
return (md) ? (md->precedence) : UNPRECEDENTED_OPERATOR;
}
@ |TRUE| if the operator is right associative, |FALSE| if it is left associative;
irrelevant, of course, for unary operators.
=
int I6Operators::right_associative(inter_ti BIP) {
i6_operator_metadata *md = I6Operators::operator_for_BIP(BIP);
return (md) ? (md->right_associative) : TRUE;
}