1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-05 16:44:21 +03:00
inform7/services/calculus-module/Chapter 4/Terms.w
2021-04-09 00:04:34 +01:00

221 lines
8.1 KiB
OpenEdge ABL

[Terms::] Terms.
Terms are the representations of values in predicate calculus:
variables, constants or functions of other terms.
@h About terms.
A "term" can be a constant, a variable, or a function of another term: see
//What This Module Does//. Our data structure therefore falls into three
cases. At all times exactly one of the three relevant fields, |variable|,
|constant| and |function| is used.
(a) Variables are represented by the numbers 0 to 25, and |-1| means
"not a variable".
(b) Constants are pointers to |specification| structures of main
type |VALUE|, and |NULL| means "not a constant".
(c) Functions are pointers to |pcalc_func| structures (see below), and
|NULL| means "not a function".
Cinders are discussed in //imperative: Cinders and Deferrals//, and can be ignored for now.
In order to verify that a proposition makes sense and does not mix up
incompatible kinds of value, we will need to type-check it, and one part
of that involves assigning a kind of value $K$ to every term $t$ occurring
in the proposition. This calculation does involve some work, so we cache
the result in the |term_checked_as_kind| field.
=
typedef struct pcalc_term {
int variable; /* 0 to 25, or |-1| for "not a variable" */
struct parse_node *constant; /* or |NULL| for "not a constant" */
struct pcalc_func *function; /* or |NULL| for "not a function of another term" */
int cinder; /* complicated, this: used to worry about scope of I6 local variables */
struct kind *term_checked_as_kind; /* or |NULL| if unchecked */
} pcalc_term;
@ The |pcalc_func| structure represents a usage of a function inside a term.
Terms such as $f_A(f_B(f_C(x)))$ often occur, an example which would be stored
as:
(1) A |pcalc_term| structure which has a |function| field pointing to
(2) A |pcalc_func| structure whose |bp| field points to A, and whose |fn_of|
field is
(3) A |pcalc_term| structure which has a |function| field pointing to
(4) A |pcalc_func| structure whose |bp| field points to B, and whose |fn_of|
field is
(5) A |pcalc_term| structure which has a |function| field pointing to
(6) A |pcalc_func| structure whose |bp| field points to C, and whose |fn_of|
field is
(7) A |pcalc_term| structure which has a |variable| field set to 0 (which is $x$).
=
typedef struct pcalc_func {
struct binary_predicate *bp; /* the predicate B */
int from_term; /* which term of the predicate this derives from */
struct pcalc_term fn_of; /* the term to which we apply the function */
} pcalc_func;
@ Terms are really quite simple, as the following //calculus-test// exercise shows:
= (text from Figures/terms.txt as REPL)
@h Creating new terms.
=
pcalc_term Terms::new_variable(int v) {
pcalc_term pt; @<Make new blank term structure pt@>;
if ((v < 0) || (v >= 26)) internal_error("bad variable term created");
pt.variable = v;
return pt;
}
pcalc_term Terms::new_constant(parse_node *c) {
pcalc_term pt; @<Make new blank term structure pt@>;
pt.constant = c;
return pt;
}
pcalc_term Terms::new_function(struct binary_predicate *bp, pcalc_term ptof, int t) {
if ((t < 0) || (t >= MAX_ATOM_ARITY)) internal_error("term out of range");
pcalc_term pt; @<Make new blank term structure pt@>;
pcalc_func *pf = CREATE(pcalc_func);
pf->bp = bp; pf->fn_of = ptof; pf->from_term = t;
pt.function = pf;
return pt;
}
@ Where, in all three cases:
@<Make new blank term structure pt@> =
pt.variable = -1;
pt.constant = NULL;
pt.function = NULL;
pt.cinder = -1; /* that is, no cinder */
pt.term_checked_as_kind = NULL;
@h Copying.
=
pcalc_term Terms::copy(pcalc_term pt) {
if (pt.constant) pt.constant = Node::duplicate(pt.constant);
if (pt.function) pt = Terms::new_function(pt.function->bp,
Terms::copy(pt.function->fn_of), pt.function->from_term);
return pt;
}
@h Variable letters.
The number 26 turns up quite often in this chapter, and while it's normally
good style to define named constants, here we're not going to. 26 is a number
which anyone[1] will immediately associate with the size of the alphabet.
Moreover, we can't really raise the total, because we will want to compile
these with single-character identifier names, |a| to |z|.[2] To have a
variable limit lower than 26 would be artificial, since there are no memory
constraints arguing for it; but a proposition with 27 or more variables would
be too huge to evaluate at run-time in any remotely plausible length of time.
So although the 26-variables-only limit is embedded in Inform, it really is
not any restriction, and it greatly simplifies the code.
[1] Well, perhaps not a string theorist. "There aren't enough small numbers to
meet the many demands made of them" (Richard Guy).
[2] Strictly speaking there is also |_|, but we won't go there.
@ The variables 0 to 25 are referred to by the letters $x, y, z, a, b, c, ..., w$,
as provided for by this lookup array:
=
char *pcalc_vars = "xyzabcdefghijklmnopqrstuvw";
@h Underlying terms.
Routines to see if a term is a constant $C$, or if it is a chain of functions
at the bottom of which is a constant $C$; and similarly for variables.
=
parse_node *Terms::constant_underlying(pcalc_term *t) {
if (t == NULL) internal_error("null term");
if (t->constant) return t->constant;
if (t->function) return Terms::constant_underlying(&(t->function->fn_of));
return NULL;
}
int Terms::variable_underlying(pcalc_term *t) {
if (t == NULL) internal_error("null term");
if (t->variable >= 0) return t->variable;
if (t->function) return Terms::variable_underlying(&(t->function->fn_of));
return -1;
}
@h Adjective-noun conversions.
As we shall see, a general unary predicate stores a type-reference
pointer to an adjectival phrase -- the adjective it tests. But
sometimes the same word acts both as adjective and noun in English. In
"the green door", clearly "green" is an adjective; in "the door
is green", it is possibly a noun; in "the colour of the door is
green", it must surely be a noun. Yet these are all really the same
meaning. To cope with this ambiguity, we need a way to convert the
adjectival form of such an adjective into its noun form, and back
again.
=
#ifdef CORE_MODULE
pcalc_term Terms::adj_to_noun_conversion(unary_predicate *tr) {
adjective *aph = AdjectivalPredicates::to_adjective(tr);
instance *I = AdjectiveAmbiguity::has_enumerative_meaning(aph);
if (I) return Terms::new_constant(Rvalues::from_instance(I));
property *prn = AdjectiveAmbiguity::has_either_or_property_meaning(aph, NULL);
if (prn) return Terms::new_constant(Rvalues::from_property(prn));
return Terms::new_variable(0);
}
#endif
@ And conversely:
=
unary_predicate *Terms::noun_to_adj_conversion(pcalc_term pt) {
#ifdef CORE_MODULE
parse_node *C = pt.constant;
if (Node::is(C, CONSTANT_NT) == FALSE) return NULL;
kind *K = Node::get_kind_of_value(C);
if (Properties::property_with_same_name_as(K) == NULL) return NULL;
if (Kinds::Behaviour::is_an_enumeration(K)) {
instance *I = Node::get_constant_instance(C);
return AdjectivalPredicates::new_up(Instances::as_adjective(I), TRUE);
}
#endif
return NULL;
}
@h Writing to text.
The art of this is to be unobtrusive; when a proposition is being logged,
we don't much care about the constant terms, and want to display them
concisely and without fuss.
=
void Terms::log(pcalc_term *pt) {
Terms::write(DL, pt);
}
void Terms::write(text_stream *OUT, pcalc_term *pt) {
if (pt == NULL) {
WRITE("<null-term>");
} else if (pt->constant) {
parse_node *C = pt->constant;
if (pt->cinder >= 0) { WRITE("const_%d", pt->cinder); return; }
if (Wordings::nonempty(Node::get_text(C))) { WRITE("'%W'", Node::get_text(C)); return; }
#ifdef CORE_MODULE
if (Node::is(C, CONSTANT_NT)) {
instance *I = Rvalues::to_object_instance(C);
if (I) { Instances::write(OUT, I); return; }
}
#endif
Node::log_node(OUT, C);
} else if (pt->function) {
binary_predicate *bp = pt->function->bp;
i6_schema *fn = BinaryPredicates::get_term_as_fn_of_other(bp, 1-pt->function->from_term);
if (fn == NULL) internal_error("function of non-functional predicate");
Calculus::Schemas::write_applied(OUT, fn, &(pt->function->fn_of));
} else if (pt->variable >= 0) {
int j = pt->variable;
if (j<26) WRITE("%c", pcalc_vars[j]); else WRITE("<bad-var=%d>", j);
} else {
WRITE("<bad-term>");
}
}