REF IDENT John Gibson May 1995
COPYRIGHT University of Sussex 1995. All Rights Reserved.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<< >>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<< IDENTIFIERS >>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<< >>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
This REF file deals comprehensively with all aspects of identifiers in
Poplog; from their types to their structure. Explanation is also given
of their dual-like nature with words and their relation to undef
records.
CONTENTS - (Use <ENTER> g to access required sections)
1 Introduction
1.1 Kinds of Identifiers
1.2 Active Identifiers
1.3 Weak Declarations for Permanent Identifiers
2 Predicates On Identifiers
3 Constructing Identifiers
4 Accessing Information About Identifiers
5 Manipulating Values of Identifiers
6 Manipulating Attachment of Permanent Identifiers to Words
7 Undef Records
8 Miscellaneous
9 Pop-11 Syntax For Use With Identifiers
---------------
1 Introduction
---------------
Identifier records are structures that represent program variables and
constants. In general, they contain the following information:
¤ A field for holding the value of the identifier (its idval, or
valof when accessed via a word).
¤ Type information that restricts what objects can be assigned to
the identifier (its identtype).
¤ A flag saying whether the identifier is an active variable or
not, and if so, a field containing its multiplicity (see Active
Identifiers below).
¤ The syntactic properties of the identifier for the Pop-11
compiler, which tell the latter how to interpret an occurrence
of the identifier name in a program (its identprops).
¤ An indication of whether the identifier is lexical or permanent
(see below).
Although identifier records can be manipulated in their own right,
program variables and constants are always specified to compilers (and
to the Poplog Virtual Machine) by name, i.e. by word records which have
identifiers associated with them (see REF * WORDS).
1.1 Kinds of Identifiers
-------------------------
Identifiers used in a program (and declared to the Poplog VM) are of two
kinds: permanent (declared with vars or constant), and lexical (declared
with lvars or lconstant). The latter are different from the former in
that their scope is not indefinite, that is, a lexical identifier only
exists while the file or procedure that defines it is being compiled.
Permanent identifiers, on the other hand, have indefinite scope and
exist until they are cancelled.
For this reason, the attachment from words to identifiers is
maintained differently for the two kinds. For lexical identifiers, the
Poplog VM holds an association list of words to identifier records,
entries being deleted from this list when the scopes of identifiers
terminate. For permanent identifiers, the association is achieved by
making an actual field in the word record point directly to the
identifier. (However, a given word can be associated with different
permanent identifiers in different program sections, see
REF * SECTIONS.)
Because of the duality between words and identifiers, some of the
procedures described below can take either an identifier or a word as
argument (this is indicated by the argument name wident as opposed to
ident for an identifier-only argument). However, because lexical
declarations exist only at compile-time, these procedures when given a
word argument will extract only a PERMANENT identifier attached to the
word. (The only way of accessing a lexical identifier associated with a
word is via sys_current_ident, described in REF * VMCODE.)
1.2 Active Identifiers
-----------------------
Active identifiers generalise the notion of a identifier with an
associated value by allowing the actual value slot in an identifier
record to contain not the associated value itself, but a procedure that
will return that value when called. Thus, when an identifier is declared
active, attempting to access its value will cause the procedure found in
the value slot to be executed, and its result returned. Similarly,
attempting to assign to the variable will run the updater of that
procedure with the new value passed as its argument.
Moreover, the mechanism is generalised still further by allowing the
"nonactive" procedure and its updater to have not just one
result/argument, but any fixed number of them: this number is called the
multiplicity of the active identifier. An access to an active identifier
of multiplicity M therefore produces M results, and a similar number
must be given when assigning to it.
Various means are provided for referencing the actual procedure
without executing it (e.g. nonactive_idval). Note that an active
identifier is usually a variable, in the sense that the procedure
result(s) are not constant (although the nonactive procedure value
itself may be either constant or variable); however, it is also possible
to have an active constant, where the nonactive procedure does not have
an updater (and the base procedure always returns the same value(s)).
1.3 Weak Declarations for Permanent Identifiers
------------------------------------------------
Permanent identifiers support a weak mode of declaration in which the
identifier is not considered to have been fully 'defined' (in Pop-11
this is represented by the syntax weak constant ... or weak vars ...,
etc).
The purpose of this mode is to enable the attachment of an
identifier record to a word record so that its section context,
identprops and identtype, etc, are established, but in a way that does
not prevent operations which access the value of the identifier from
treating it as if it were still undefined (and thus does not prevent
autoloading of the value, for example).
In particular (in conjunction with a Poplog VM facility for weak
references to identifiers which does not insist on their being fully
defined), it allows programs to declare and conditionally reference
identifiers which may or may not be present at run-time. (This includes
optionally-present protected system identifiers for which a normal
declaration would mishap, since weak declarations for identifiers which
are already defined are simply ignored.)
Another facility (available through the Poplog VM and via the Pop-11
syntax construct weakref) also allows the declared/defined status of one
identifier to be made dependent on a set of others, in the sense that if
any member of the dependency set becomes fully defined, then this forces
the dependent identifier to be defined also (i.e. as if a 'strong'
reference to it had occurred).
----------------------------
2 Predicates On Identifiers
----------------------------
isident(item) -> kind [procedure]
Returns a true result if item is an identifier record, or false
if not. The true result is a word which indicates the kind of
identifier, as follows:
Word Identifier Type
---- ---------------
"perm" permanent identifier
"lex" 'real' lexical identifier
"lextoken" 'token' lexical identifier
(The difference between "lex" and "lextoken" is that a "lex" is
a real identifier record whose idval is its actual run-time
value, whereas a "lextoken" is a record being used by the Poplog
VM to represent a procedure-local lexical variable, and which
will not necessarily play any part in the final compiled code
for the procedure.)
isdeclared(wident) -> ident [procedure]
Given an identifier (of any kind), this procedure just returns
its argument. For wident a word with a permanent identifier
currently attached the (true) result is that identifier,
otherwise false for an undeclared word. (The truth value of the
result is thus identical to identprops(wident) /== "undef".)
This procedure differs from isdefined in that it returns
identifiers which have only weak declarations (see above). (In
fact, this is the only way to access such an identifier from a
word, since identof will treat one as undefined.)
isdefined(wident) -> ident [procedure]
Same as isdeclared, except that false is returned for a
permanent identifier having only a weak declaration. (Thus
identprops(wident) /== "undef" does NOT guarantee that
isdefined(wident) is true.)
isassignable(wident) -> bool [procedure]
isconstant(wident) -> bool [procedure]
isprotected(wident) -> bool [procedure]
These predicates test extra status information for the
identifier (or permanent identifier extracted from the word)
wident:
¤ isassignable returns true if wident can have its value updated
(with idval etc, or a program assignment statement), false if
not.
¤ isconstant returns a true result if wident is declared as a
constant, false if not. In the first case, the result is true
if a value has been assigned to the constant, and the word
"undef" otherwise (note that isassignable is true for a
constant not yet assigned to).
¤ isprotected returns true if wident has been declared as a
protected identifier, false if not.
Note that by default, these procedures all return information
for the active value of an active identifier (if an active
identifier has no updater, then isconstant and isprotected
return true, and isassignable returns false).
The status of the nonactive value can be got by wrapping wident
in a 'nonactive pair', i.e.
conspair(wident, "nonactive")
isglobal(wident) -> bool [procedure]
Returns true if wident is declared as a global permanent
identifier (that is, an identifier which will be automatically
imported into daughter sections below its level), false
otherwise.
isactive(wident) -> mult_or_false [procedure]
Given an identifier (or permanent identifier extracted from the
word) wident, this procedure returns a true value if the
identifier is declared active, or false if not. The true result
returned is the active multiplicity of the identifier, i.e. an
integer in the range 0-255.
isdlocal(wident, p) -> bool [procedure]
Given a word or identifier wident and a procedure p, this
procedure returns true if wident is a dynamic local of p (i.e.
declared dlocal in p), or false otherwise.
If wident is an active variable, then by default the result
reflects whether its active value is local; if wident is a
variable active variable, and you wish to test whether its
nonactive value is local, wrap wident in a 'nonactive pair',
i.e.
conspair(wident, "nonactive")
---------------------------
3 Constructing Identifiers
---------------------------
ident_declare(word, idprops, const_mode) [procedure]
Declares the word word as a permanent constant/variable
identifier with identprops idprops in the current section.
The const_mode argument is an integer in which bit 0 determines
whether the identifier is constant or variable, and bit 1
whether this a weak declaration or not (for an explanation of
which, see Weak Declarations for Permanent Identifiers above).
I.e.
Bit Meaning
--- -------
0 0 = variable, 1 = constant
1 0 = ordinary declaration, 1 = weak declaration
Alternatively, const_mode may be a boolean, true and false
representing constant/variable in ordinary mode (i.e. 2:01 and
2:00 respectively).
Permissible values for the idprops argument are
Value Meaning
----- -------
0 Untyped identifier.
"procedure" Procedure-type identifier.
N Pop-11 operation of precedence N, where N is
an integer or floating-point in the range
-12.7 to 12.7 .
"macro" Pop-11 macro.
"syntax" Pop-11 syntax word.
"syntax N" Pop-11 syntax operator of precedence N, range
as above.
(Note that "syntax N" is not a normal itemisable word in Pop-11,
and requires additional string quotes to itemise it, e.g.
"'syntax 3'" .)
To declare an active variable, idprops may also be a pair whose
front is any of the above identprops values, and whose back is
the active multiplicity. For an ordinary untyped variable
(identprops value 0), the multiplicity may be an integer in the
range 0 - 255; for any other identprops, it must be 1. The
const_mode argument in this case refers to the constancy or
otherwise of the nonactive procedure value of the identifier
(its active value(s) are always variable).
If word was previously undeclared, the attached identifier has
its idval initialised to a newly-created undef record whose
undefword is word; by default for an existing identifier, the
idval is left undisturbed but the identprops etc are changed to
reflect the new declaration (except for a weak declaration,
which is ignored).
Note that when a constant is declared, the Poplog VM effectively
treats it as a variable until a value is assigned to it (after
which, attempting to assign to it again produces a mishap); this
means that 'forward' declarations and uses of constants in code
will actually treat the identifier as a variable. Also by
default, the same applies when an existing constant is
redeclared (ensuring that recompiled code does not continue to
use the old value of a constant for which a new value will
recompiled later).
However, when pop_debugging is false, and the flag
VM_PERM_FIXED_DECLARE is set in * pop_vm_flags, then any
redeclaration for an existing identifier must agree with its
current declaration (in respect of both constant/variable and
identprops), otherwise a mishap results. Moreover, in this mode
redeclarations of constants do not reset them to 'unassigned'
(variable) status (thus permitting a file in a multi-file
program to have header declarations for constants defined in
prior-loaded files, without losing the benefit of using the
constant values).
Note also that when pop_debugging has the value true (its
default), the Poplog VM treats all user-defined permanent
constants as variables. See * pop_debugging.
consident(idprops, const_mode, kind) -> ident [procedure]
This procedure gives a way to construct identifier records
directly, without the associated declaration of a word.
The idprops argument specifies the identprops and/or activeness
of the identifier: permissible values are as for ident_declare
above.
The const_mode argument is also as for ident_declare.
kind specifies the kind of identifier as returned by isident
(see above); permissible values are the words "perm", "lex" and
"lextoken".
------------------------------------------
4 Accessing Information About Identifiers
------------------------------------------
See also the is- predicates above for other properties of identifiers.
identprops(wident) -> idprops [procedure]
Returns the identprops (macro/Pop-11 syntax properties) of the
identifier (or permanent identifier extracted from the word)
wident. This can take the following values:
Value Meaning
----- -------
"undef" wident is a word not declared as a permanent
identifier (i.e. for which isdeclared is
false).
N Pop-11 operation of precedence N, where N is
an integer or floating-point in the range
-12.7 to 12.7 .
"macro" Pop-11 macro.
"syntax" Pop-11 syntax word.
"syntax N" Pop-11 syntax operator of precedence N, range
as above.
0 An "ordinary" permanent identifier, i.e. one
that has no special syntactic properties.
(Note that "syntax N" is not a normal itemisable word in Pop-11,
and requires additional string quotes to itemise it, e.g.
"'syntax 3'" .)
identtype(wident) -> type [procedure]
Returns the data type of the identifier (or permanent identifier
extracted from the word) wident. Possible values are
Value Meaning
----- -------
"undef" wident is a word not declared as a permanent
identifier (i.e. for which isdeclared is
false).
0 Untyped, i.e. may hold anything.
"procedure" May hold procedures only.
full_identprops(wident) -> idprops_list [procedure]
Returns a list of all the declaration keywords for the
identifier (or permanent identifier extracted from the word)
wident, or "undef" if wident is a word not declared as a
permanent identifier. The list has the form
[prot glob const/var type idprops]
where
Value Meaning
----- -------
prot "protected" for a protected identifier, empty
otherwise;
glob "global" for a global permanent identifier,
empty otherwise;
const/var "constant" for a constant (preceded by
"assignable" if the identifier can still be
assigned to), or "vars" for a variable;
type "procedure" for a procedure-type identifier,
empty otherwise;
idprops the identifier's identprops (except that 0 for
an ordinary untyped identifier is omitted, and
"syntax N" for a syntax operator is returned as
"syntax" followed by the number N).
is_syntax_word(item) -> idprops_or_false [procedure]
If item is a word whose identprops are "syntax" or "syntax N"
then returns those identprops. For any other item, returns
false.
-------------------------------------
5 Manipulating Values of Identifiers
-------------------------------------
See REF * PROCEDURE for more information on dynamic local variables of
procedures.
identof(word) -> ident [procedure]
ident -> identof(word)
Returns or updates the permanent identifier currently attached
to word.
In access mode, if word has no permanent identifier already
attached (or the attached identifier has only a weak
declaration, see above), sysdeclare is called on word to
autoload it or to provide a default definition (see REF * VMCODE
for a description of sysdeclare).
In update mode, ident must be permanent identifier (i.e.
assigning a lexical identifier is not allowed). Note that
assigning an identifier to a dictionary word or a word produced
by word_identifier is equivalent to declaring it with
ident_declare, i.e. it is registered by the section mechanism
(see REF * SECTIONS).
idval(ident) -> item [procedure]
item -> idval(ident)
Returns or updates the value cell of the identifier ident (in
update mode, item must be valid for the identtype of ident).
When ident is an active variable, idval runs the nonactive
procedure value of ident, or its updater in update mode. The
number of items produced as results by the procedure, or taken
as arguments by its updater, will then be the multiplicity M of
the variable.
nonactive_idval(ident) -> item [procedure]
item -> nonactive_idval(ident)
Returns or updates the value cell of the identifier ident,
regardless of whether the identifier is active or not.
valof(wident) -> item [procedure]
item -> valof(wident)
Where wident is an identifier, or a word with an associated
permanent identifier, returns or updates the idval of the
identifier. That is, valof is the same as idval if wident is an
identifier, and otherwise the same as
identof <> idval
i.e. identof is first applied to translate a word to an
identifier.
nonactive_valof(wident) -> item [procedure]
item -> nonactive_valof(wident)
Same as * valof, but using * nonactive_idval.
caller_valof(wident, caller) -> item [procedure]
item -> caller_valof(wident, caller)
Where wident is an identifier, or a word with an associated
permanent identifier, returns or updates the valof of wident as
it would be in the environment of the currently-active procedure
specified by caller.
The argument caller may be either
¤ An actual procedure or a caller number as input to * caller
(see REF * PROCEDURE).
¤ false, meaning that the value outside of all dynamic
localisations (i.e. outside all procedure calls) is
accessed/updated.
(From Version 14.53, this procedure works correctly with active
variables, i.e. returns or updates their active value(s).)
nonactive_caller_valof(wident, caller) -> item [procedure]
item -> nonactive_caller_valof(wident, caller)
Same as * caller_valof, but active variables have their
nonactive value returned or updated.
set_global_valof(item, wident) [procedure]
Using caller_valof, assigns item to be the value of wident in
the context of every currently active procedure for which the
identifier (or permanent identifier extracted from the word)
wident is a dynamic local.
recursive_valof(word) -> item [procedure]
Recursively applies valof to word while item is a word, and
returns the result -- that is, recursive_valof calls valof with
word as argument: if the result item is a word, then
recursive_valof is called with item as argument; if it is not a
word, item itself is returned.
------------------------------------------------------------
6 Manipulating Attachment of Permanent Identifiers to Words
------------------------------------------------------------
syssynonym(word1, word2) [procedure]
An autoloadable library procedure, defined simply as
identof(word2) -> identof(word1)
sysprotect(word) [procedure]
Protects the permanent identifier associated with word -- that
is, stops word being redeclared (i.e. given a new identifier),
and disallows any assignment to the identifier.
sysunprotect(word) [procedure]
Unprotects the permanent identifier associated with word (after
a sysprotect) -- allows the word to be redeclared (given a new
identifier), and makes possible assignment to the identifier.
syscancel(word) [procedure]
Cancels any permanent identifier currently attached to the word
word. This is registered by the section mechanism if word is a
dictionary word or a word produced by word_identifier (for
details see REF * SECTIONS).
----------------
7 Undef Records
----------------
Undef records are special records containing just one field, their
undefword, and are used by the system to initialise the values of
newly-created global identifers (although they be used for other
purposes). The undefword may be a word or false; a new permanent
identifier is initialised to a newly-constructed undef record whose
undefword is the name of the identifier, whereas new global lexical
identifiers are initialised to the fixed undef record pop_undef (with
undefword false, which prints as <undef> as opposed to <undef foo> for
one containing the word "foo").
However, since procedure-type identifiers must always have a procedure
value, they are initialised to undef-closures, which are closures of a
system error procedure, partially applied to the actual undef record for
the identifier (where the error procedure will produce an appropriate
mishap if an attempt is made to apply it). Global lexical procedure
identifiers use the standard undef-closure pop_undef_p.
Yet another type of undef-closure is used to initialise active
variables: these are such that if the variable is accessed, it will
return an actual undef record M times, where M is the multiplicity of
the variable. Assigning to the variable will simply erase M items from
the stack.
All types of undef record/closures are recognised by isundef and
undefword. See also HELP * UNDEF
isundef(item) -> bool [procedure]
Returns true if item is an undef record or an undef-closure,
false if not.
consundef(word) -> undef [procedure]
Creates a new undef record with undefword word, which may be a
word or false. For example:
consundef("cat")=>
** <undef cat>
undefword(undef) -> word [procedure]
Returns a word or false for the undef record or undef-closure
undef.
pop_undef -> undef [constant]
pop_undef_p -> undef [procedure]
The standard 'untyped' undef record and 'procedure-type'
undef-closure (used apart from other things for initialising
global lexical identifiers).
(N.B. The constant undef should really be pop_undef.
Unfortunately, historically undef has always contained the word
"undef".)
----------------
8 Miscellaneous
----------------
_ -> undef [variable]
item -> _
The 'anonymous' (active) variable _ returns pop_undef when
accessed, and discards any value assigned to it. For example,
1, 2, 3 -> (x, _, y);
will assign 3 to y, 1 to x, and discard the 2.
This variable may also appear in Pop-11 identifier declarations,
and as a formal argument or result in define headers, e.g.
lvars (x, _, y) = dl([a b c]);
define foo(arg1, _, arg3);
...
enddefine;
(Uses of this variable are recognised and fully optimised by
Poplog VM instructions such as sysPUSH, sysPOP, sysLOCAL and
sysPASSIGN, etc.)
ident_key -> key [constant]
undef_key -> key [constant]
Hold the keys for identifiers and undef records (see
REF * KEYS).
-----------------------------------------
9 Pop-11 Syntax For Use With Identifiers
-----------------------------------------
ident [syntax]
This syntax word compiles code to push an identifier onto the
stack (the identifier itself, not its value). Usage is
ident name
where name is a word declared as any kind of identifier; the
effect is to push onto the stack the (run-time) identifier for
the current declaration of name.
(ident operates by calling sysIDENT for the given word -- see
REF * VMCODE for more details.)
Note that any ident expression for a permanent identifier may be
surrounded by word quotes to get the corresponding quoted word
identifier, e.g.
"ident $-mysect$-xxxx" -> word_id;
See REF * word_identifier for more details.
weak [syntax]
Used before a vars or constant statement to make weak
declarations for permanent identifiers (see `Weak Declarations
for Permanent Identifiers' above). E.g.
weak vars foo, baz;
weak constant xxx;
weakref [syntax]
Flags a 'weak' access to a permanent identifier, i.e. one that
will not cause the identifier to become 'defined' if it is
currently only weakly declared. In Pop-11
weakref name
can occur anywhere where name by itself would constitute an
identifier reference (i.e. excluding declarations). For example,
weakref foo -> a;
weakref foo(1,2,3);
ident weakref foo -> id;
nonop weakref foo -> p;
dlocal weakref foo;
etc. (In Poplog VM terms, weakref passes the identifier name to
the appropriate VM instruction inside a 'weakref pair', e.g.
sysPUSH(conspair(name, "weakref"))
See REF * VMCODE.)
weakref has also has a more complicated form which allows the
declared/defined status of one identifier to be made dependent
on a set of others. The syntax
weakref [ dname1, dname2, ..., dnameN ] name
is identical in terms of code produced to
weakref name
but additionally declares name as being dependent on the
identifiers dname1 ... dnameN. This means that whenever any
member of the dependency set becomes fully defined (including
the case where one or more already are), then this forces the
dependent identifier name to be defined also (as if a 'strong'
reference to it had occurred). E.g.
weakref[foo, baz] grum()
makes grum dependent on the status of foo and baz. Dependency
identifiers for a given identifier accumulate across uses of
weakref, so that a later
weakref[xxx] grum()
will make grum dependent on all of foo, baz and xxx.
testdef [syntax]
Allows the declared/defined status of a permanent identifier to
be tested. The form
testdef name
is equivalent to the truth value of the expression
isdefined(ident weakref name)
Since a weakly declared identifier has an undef value,
attempting to execute it as in
weakref foo(1,2,3);
may cause a mishap; weak references should therefore normally be
protected with testdef used in a conditional, e.g.
if testdef foo then
weakref foo(1,2,3)
endif;
guarantees that foo will be executed only if fully defined. On
the other hand, uses such as
if value == weakref foo then ...
are safe without a testdef (assuming that value can never be the
undef value of foo). In particular,
dlocal weakref foo;
is always safe.
cancel [syntax]
Cancels permanent identifiers. Usage is
cancel name1, name2, ..., nameN ;
where each name is an identifier name (which can include a
section pathname). It applies syscancel to each name (but note
that it will not cancel protected identifiers -- if you want to
do this, either use sysunprotect on the word first, or use
syscancel directly).
+-+ C.all/ref/ident
+-+ Copyright University of Sussex 1995. All rights reserved.