Date: Thu, 5 Apr 90 15:13:49 -0400 From: jjw1@gte.com (James J. Walker) Subject: Generated code for lib/fingerprint/PolyBasis.ic I am beginning to port Modula-3 version 1.4 to the Apollo platform, and ran into a problem that I did not have with version 1.2. (By the way, has anyone else attempted this port??? I would be interested in comparing notes!) I built the cross-compiler on a DS3100, generated the C files, and moved it all over to the Apollo, and started the compile (using the Domain C compiler). The compile bombed on lib/fingerprint/PolyBasis.ic. The generated code in this file looked something like the following (abstracted for brevity)... typedef int t6; typedef struct { t6 elts[2] } t2; typedef struct { t2 elts[256] } t4; EXPORT t4 PolyBasis__poly64 = { { 0 }, { ..., ...}, ... }; ... The Domain C compiler will not accept the static initialization above because it is missing a level of curly brackets; i.e., it expects one level for the structure t4, one level for the array elts[256], and one level for each of the arrays elts[2]. The interesting thing is that this same file will compile on a VAX (and I assume a DS3100). So I guess there are a couple of questions. First, which compiler is right? Does anyone know what the ANSI-C folks have to say about this? Second, assuming that the Domain C compiler is right (that another level of curly brackets is needed), where would I look in the Modula-3 compiler stuff to change the generated code for the static initialization of structures like this? (P.S. I got the file to compile by adding the extra levels of curly brackets where needed by hand. I am sure I will be back shortly with more questions!) Jim Walker UUCP: ..!harvard!bunny!jjw1 GTE Laboratorie, Waltham, MA CSNET: jjw1@gte-labs ------------------------------------------------------------------------------ Date: Thu, 5 Apr 90 14:04:28 -0600 From: drh%austin@cs.utah.edu (David Hanson) Subject: Re: Generated code for lib/fingerprint/PolyBasis.ic Date: Thu, 5 Apr 90 15:13:49 -0400 From: jjw1@gte.com (James J. Walker) ... The compile bombed on lib/fingerprint/PolyBasis.ic. The generated code in this file looked something like the following (abstracted for brevity)... typedef int t6; typedef struct { t6 elts[2] } t2; typedef struct { t2 elts[256] } t4; EXPORT t4 PolyBasis__poly64 = { { 0 }, { ..., ...}, ... }; ... The Domain C compiler will not accept the static initialization above because it is missing a level of curly brackets; i.e., it expects one level for the structure t4, one level for the array elts[256], and one level for each of the arrays elts[2]. The interesting thing is that this same file will compile on a VAX (and I assume a DS3100). So I guess there are a couple of questions. First, which compiler is right? Does anyone know what the ANSI-C folks have to say about this? Second, assuming that the Domain C compiler is right (that another level of curly brackets is needed), where would I look in the Modula-3 compiler stuff to change the generated code for the static initialization of structures like this? the Domain C compiler is correct w.r.t. ANSI C. the problem is that the { 0 } initializes the *entire* array of elts[256] in the top-level structure, so the the compiler probably barks about too many initializers, eg, austin 25 lcc -S PolyBasis.c PolyBasis.c:261: too many initializers ... i assume you added a set of {} to enclose the initialization of the elts[256] a rray, ie, you changed the declaration to read EXPORT t4 PolyBasis__poly64 = { { { 0 }, { ..., ...}, ... } }; it's also legal to omit *all* the {}'s if you provide an extra 0 for the first t2, ie, EXPORT t4 PolyBasis__poly64 = { 0, 0, ..., ..., ... }; this, too, conforms to ANSI C. does the Domain C compiler buy this variant? dave hanson (really drh@princeton.edu) ------------------------------------------------------------------------------ Date: Sun, 8 Apr 90 15:34:06 PDT From: cohesive!kla!cocos!pat@Sun.COM (Pat Lashley) Subject: Pretty Postscript The `pretty-printer' in the m3 distribution is really just a source file reformatter. The lex program below is intended to be used with pps (from comp.sources.unix Volume 17) to produce pretty output on a PostScript printer. What it does: 1) Uses different fonts for comments, strings, keywords, and program text. (For m3, pragmas are considered as comments.) The default fonts are: Comments New Century Schoolbook Strings Courier Oblique Keywords Courier Bold Other text Courier 2) Prints the date, time, filename, and page number at the top of each page. 3) Marks every tenth line by placing the line number at the right of the page. 4) Marks the beginning of a procedure by printing the procedure name vertically at the right of the page. What it doesn't: 1) Re-format the input. ------------------------------------------------------------------------------- - #!/bin/sh # to extract, remove the header and type "sh filename" if `test ! -s ./m3.l` then echo "writing ./m3.l" cat > ./m3.l << '\Rogue\Monster\' %{ #ifndef lint static char rcsid[] = "$Header: c.l,v 0.0 88/06/22 05:22:01 on Rel $"; #endif #include "pps.h" #include int nbrace; int commentDepth = 0; int needId = 0; %} letter [A-Za-z] digit [0-9] idchar [A-Za-z0-9_] white [ \t] %Start COMMENT STRING1 STRING2 KEYWORD COMMENT2 PROCNAME %% {letter}{idchar}* { int kw = iskw(yytext); if (needId) { funct(yytext); needId = 0; } if (kw) begin(KEYWORD); ECHO; if (kw) { begin(INITIAL); if (strcmp (yytext, "END") == 0) { --nbrace; } else if (strcmp (yytext, "PROCEDURE") == 0) { ++needId; } else if (strcmp (yytext, "MODULE") == 0) { /* * REMIND: Fix ECHO to do module.proc */ --needId; } else if ((strcmp (yytext, "BEGIN") == 0) || (strcmp (yytext, "CASE") == 0) || (strcmp (yytext, "FOR") == 0) || (strcmp (yytext, "IF") == 0) || (strcmp (yytext, "LOOP") == 0) || (strcmp (yytext, "LOCK") == 0) || (strcmp (yytext, "TYPECASE") == 0)|| (strcmp (yytext, "TRY") == 0) || (strcmp (yytext, "WHILE") == 0) || (strcmp (yytext, "WITH") == 0)) { ++nbrace; } } } \" { begin(STRING1); ECHO; } \' { begin(STRING2); ECHO; } \(\* { begin(COMMENT); ++commentDepth; ECHO; } \<\* { begin(COMMENT2); ECHO; } \\. { ECHO; } \\. { ECHO; } \" { ECHO; begin(INITIAL); } \' { ECHO; begin(INITIAL); } \(\* { ECHO; ++commentDepth; } \*\) { ECHO; --commentDepth; if (!commentDepth) { begin(INITIAL);} } \*\> { ECHO; begin(INITIAL); } [\t\n\f]+ { space(yytext); } \{ { nbrace++; REJECT; } \} { nbrace--; REJECT; } . { ECHO; } %% /* * This should better be sorted by frequency. */ char *keywords[] = { " ", "AND", "ARRAY", "BEGIN", "BITS", "BRANDED", "BY", "CASE", "CONST", "DIV", "DO", "ELSE", "ELSIF", "END", "EVAL", "EXCEPT", "EXCEPTION", "EXIT", "EXPORTS", "FINALLY", "FOR", "FROM", "IF", "IMPORT", "IN", "INTERFACE", "LOCK", "LOOP", "METHODS", "MOD", "MODULE", "NOT", "OBJECT", "OF", "OR", "PROCEDURE", "RAISE", "RAISES", "READONLY", "RECORD", "REF", "REPEAT", "RETURN", "REVEAL", "ROOT", "SET", "THEN", "TO", "TRY", "TYPE", "TYPECASE", "UNSAFE", "UNTIL", "UNTRACED", "VALUE", "VAR", "WHILE", "WITH", NULL }; \Rogue\Monster\ else echo "will not over write ./m3.l" fi echo "Finished archive 1 of 1" exit ------------------------------------------------------------------------------- - ------------------------------------------------------------------------------ Date: Mon, 09 Apr 90 23:00:27 PDT From: Eric Muller Subject: MODULE -> IMPLEMENTATION I think that the term "module" is commonly used to name the implementation of an interface or the pair interface/implementation, or even a group of interfaces and implementations (for example, when an implementation implements a general-user interface and a "friends" interface). To remove a bit of the ambiguity, I propose that the keyword MODULE be replaced by the keyword IMPLEMENTATION. Eric Muller. ------------------------------------------------------------------------------ Date: Tue, 10 Apr 90 17:00:28 EDT From: hudson@yough.ucc.umass.edu (Rick Hudson) Subject: MODULE -> IMPLEMENTATION I think that the term "module" is commonly used to name the implementation of an interface or the pair interface/implementation, or even a group of interfaces and implementations (for example, when an implementation implements a general-user interface and a "friends" interface). To remove a bit of the ambiguity, I propose that the keyword MODULE be replaced by the keyword IMPLEMENTATION. Eric Muller. Around here module means pretty much what Modula-3 wants it to mean. I see no reason for change but do appreciate the point. It seems the report also confuses interfaces and modules. Pg. 31 of the 21 Oct 89 Modula-3 Report. "..., T is an identifier (possibly qualified by a module name) that has ..." I assume one meant "interface name" here not "module name". - Rick Hudson ------------------------------------------------------------------------------ Date: 12 Apr 1990 1838-PDT (Thursday) From: Subject: Re: MODULE -> IMPLEMENTATION Rick Hudson is correct that pg. 31 of the M3 report errs in using "module" instead of "interface". I've made a note to replace an identifier (possibly qualified by a module name)... with a (possibly qualified) identifier.... One fewer error and three fewer words, hooray. Eric's case for changing "MODULE" to "IMPLEMENTATION" might have carried the day two years ago, depending on the whims of the committee members, but I give his suggestion slim odds of prevailing at this point. ------------------------------------------------------------------------------ Date: Tue, 17 Apr 90 13:58:12 PDT From: csw@blackntan.Corp.Sun.COM (Christopher Warth) Subject: m3 on a sparc under sunos 4.1 I am trying to bring m3 up on a sparc under SunOS 4.1. I'd like to exchange mail with anyone who has tried to bring up m3 under this configuration or something similar. I followed the instructions, did a "make SPARC" followed by "make". imake/m3make consistently fails because imake can't find Imake.tmpl. The first imake generated by m3make is OK but the recursive imake evidentally does not have the right -I directive. $ make (cd imake; cc -o imake imake.c) imake/m3make -v target if ( 0 ) then .ROOT/imake/imake -I.ROOT/imake/includes -T.ROOT/imake/Imake.tmpl \ -e -I. M3BRANCH=dev MAKE=.ROOT/imake/m3make -v target cpp -I. -Uunix -I.ROOT/imake/includes -I. make M3BRANCH=dev MAKE=.ROOT/imake/m3make target \ -f /tmp/Imf.a11810 MAKE=.ROOT/imake/imake MAKEFILE=Imakefile 4: Can't find include file Imake.tmpl .ROOT/imake/imake: Exit code 2. Stop. *** Error code 1 make: Fatal error: Command failed for target `build-tools' .ROOT/imake/imake: Exit code 1. Stop. (I added the -v flag to the m3make line so I could see what was going on.) I hacked imake to get past this problem but then all m3make does is a recursive imake, no make. That only takes a couple of minutes, certainly not the 25 minutes implied by the table in the instructions. I tried to go through the source and make what was necessary by hand but then I keep hitting the following error in m3/compiler/: --- SPARC done on Mon Apr 16 15:45:40 PDT 1990 (cd o.TARGET; ld -r -X -o .compiler.o ../?*/o.TARGET/.*.o) .ROOT/driver/m3 -Y3.ROOT/loader/m3ld -Y4.ROOT/loader/m3ld2 -o m3compiler.ta rget o.TARGET/.compiler.o \ -L .ROOT/runtime/m3run.target.o .ROOT/lib/m3lib.target.a ld: ./o.TARGET/.compiler.o: bad secondary magic number nm: m3ld_8426_i.o: cannot open ld: m3ld_8426_i.o: No such file or directory Thinking that one of the executables got corrupted along the way I cleared out all the o.TARGET's and started again with the same results. I'll keep playing with this. I just hope there is someone else out there who has tried this and might lend some advice. -csw ------------------------------------------------------------------------------ Date: Wed, 18 Apr 90 09:35:46 PDT From: David Goldberg Subject: Re: m3 on a sparc under sunos 4.1 The problem is that the install assumes that for a make invocation of make MAKE=x MAKE=y that the MAKE=x assignments wins. In SunOS 4.1, they changed make so that the MAKE=y assigment wins. The simplest workaround is to use the 'make' from SunOS 4.0.3. David Goldberg goldberg@parc.xerox.com ------------------------------------------------------------------------------ Date: Thu, 19 Apr 90 12:43:22 PDT From: Eric Muller Subject: archive of the SRC Modula-3 mailing list I have collected all the messages sent to the SRC Modula-3 mailing list. They are available in gatekeeper.dec.com:pub/DEC/Modula-3/mail.archive.{1,2,3} Future messages will find their way there, too. Eric. ------------------------------------------------------------------------------ Date: 19 Apr 1990 1356-PDT (Thursday) From: Subject: Modula 3 binding to POSIX This message contains a proposed Modula 3 binding to IEEE POSIX 1003.1 and portions of IEEE POSIX 1003.4A. These interfaces have not been implemented but they are similar enough to the Topaz Modula 2+ Unix interfaces (see SRC report 21) so that I don't think there should be many problems. At this point I am interested in both comments on the interface itself and volunteers who would willing to dive into the Modula 3 runtime and implement these interfaces layered on a BSD based POSIX system. While I can provide consulting help (and possibly the sources for the layered Modula 2+ based interface) I don't have time to actually do the work myself. Thanks to Paul McJones and John Ellis for their comments. Garret Swart (415) 853-2220 ------- (* File: POS.i3 *) (* Last modified on Thu Apr 19 13:42:15 PDT 1990 by swart *) INTERFACE POS; IMPORT Text, Rd, Time; FROM Thread IMPORT Alerted; (* This interface is a native Modula 3 interface to the facilities of IEEE POSIX 1003.1-1988 and some of the facilities of the, as yet unballoted, POSIX 1003.4a-1990 (thread extensions). This interface is designed to be modified for each POSIX implementation, but in a way so that applications using this interface in a portable way will still compile and run. The permissible changes that may be made to this interface and the rules for writing portable code are given here and in the relevant POSIX standards. While it is possible to define a compatible C style binding for the POSIX interface, it is deemed preferable to define a native Modula 3 interface. This interface should work more smoothly with the Modula 3 programming style. Changes were made to take advantage of the following Modula 3 features: objects, threads, enumerated types, sets, TEXTs, plus we have used Modula 3 spelling conventions. Pervasive changes from the C binding include: Changing each procedure that may set the errno variable to instead raise the Error exception. Changing each procedure that may set errno to EINTR to raise the Thread.Alerted exception. Replacing all bit masks with sets. Attempts were made to allow the Modula 3 structures to be binary compatible with the C types, and where that was not possible, a note was made. Replacing those facilities of POSIX that are already present in M3. These include the Thread, Time and Params interfaces. All operations of open files have been added to various methods on subtypes of the type POS.File. The various fcntl operations have been split into separate methods for better use of the type system. Note that setting the process state in one thread may adversely affect other threads in the process using that state. This can be a problem for the signal state as well as the current working directory and the effective user IDs. The related interfaces POSTerminal and POSPath give operations for dealing with POSIX terminals and pathnames. This particular version of the POS interface has been adjusted to support the Ultrix 3.1 version of POSIX. To distinguish all names that are not part of the Modula 3 binding of the POSIX standard, they have been given the suffix "_NP", meaning "Not POSIX". In all enumerated types, the order of the elements may be changed, nonportable names removed, and new nonportable names added. BITS FOR may be added or removed from any type. Wherever a record appears, the orders of the fields may be changed, nonportable fields may be removed and new nonportable fields added. Optional parameters to procedures may be added after all the portable parameters and the parameter names should end with _NP. Arguments may be added to exceptions that have no standard arguments. The argument type must end with _NP. Note that this interface does not describe all of the differences between POSIX and Ultrix 3.1. This interface is limited to only those parts of Ultrix that are needed to completely define the POSIX types. Other Ultrix specific functions are in other interfaces. This interface is rather sparingly commented. It is expected that the programmer has a copy of IEEE 1003.1-1988 available for reference. *) (* Differences between POS and the Modula 2+ OS interface. The OS interface was developed by Paul McJones and Garret Swart to support multithreaded access to the facilities of the Ultrix and Taos operating systems. The OS interface goes farther than this interface in a number of ways. Directory handles. The OS interface allows a process to open a number of directory handles simultaneously, rather than the single working directory allowed by POSIX. Each operation accepting a path also accepts a directory handle that the path is evaluated relative to. This facility is used rather infrequently. Typically in single process/multi-window shells and in servers. However many user programs don't need this facility and are happier with the late binding semantics of keeping directories textually. User handles. The OS interface allows a process to act on behalf of several users simultaneously by allowing an optional User authorization parameter on each call that potentially does something that needs to be authenticated. This is primarily useful for servers. Names instead of IDs. The OS interface deals only with user and group names rather than IDs. This allows scaling to large systems that are not in one administrative domain. The new OS1 interface goes still farther: Defines a super root for access to files in a global name space. Defines new operations useful for managing information stored in a name service. Defines a more flexible access control scheme using access control lists. *) (* Primitive system data types. *) (* The following types may differ on an implementation to implementation basis. The rules regarding permissible bindings for these types is given in the POSIX standards. Those types that are opaque in the POSIX sense have been given the suffix "_T" *) TYPE Short_NP = [-16_8000 .. 16_7FFF]; TYPE Device_T = [0 .. 16_FFFF]; GroupID_T = Short_NP; UserID_T = Short_NP; INode_T = INTEGER; NLink_T = Short_NP; Offset_T = INTEGER; PID_T = INTEGER; Clock_T = INTEGER; (* Units of time. Each unit represents 1/ GetSystemConfigurationParameter(SystemConfigurationParameter.ClockTick) seconds. *) (* Environment description *) (* See Params.i3 *) (* Limits. *) (* Minimum values. These should change only when the POSIX standard changes. *) CONST POSIX_ArgumentMax = 4096; POSIX_ChildMax = 6; POSIX_LinkMax = 8; POSIX_MaxCanonical = 255; POSIX_MaxInput = 255; POSIX_NameMax = 14; POSIX_NGroupsMax = 0; POSIX_OpenMax = 16; POSIX_PathMax = 255; POSIX_PipeBuf = 512; (* Implementation defined maximum values. These are the Ultrix 3.1 values *) CONST ArgumentMax = 10240; ChildMax = 25; LinkMax = 1000; MaxCanonical = 256; MaxInput = 256; NameMax = 255; NGroupsMax = 32; OpenMax = 64; PathMax = 1024; PipeBuf = 4096; (* Configurable Pathname Variables *) TYPE PathnameConfigurationParameter = {bad0_NP, LinkMax, MaxCanonical, MaxInput, NameMax, PathMax, PipeBuf, ChangeOwnerRestricted, NoTruncation, VDisable}; (* Configurable System Variables *) TYPE SystemConfigurationParameter = { bad0_NP, ArgumentMax, ChildMax, ClockTick, NGroupsMax, OpenMax, JobControl, SavedIDs, Version}; (* Constants for portability specifications *) (* These values of these constants are those for Ultrix 3.1 *) CONST POSIX_JobControl = TRUE; POSIX_SavedIDs = TRUE; POSIX_Version = 198808; CONST POSIX_ChangeOwnerRestricted = TRUE; POSIX_NoTruncation = TRUE; POSIX_VDisable = FALSE; (* Errors *) TYPE EC = {ok_NP, Permission, NoEntry, Search, Interrupt, IO, NoXIO, TooBig, NoExec, BadFile, Child, Again, NoMemory, Access, Fault, NotBlock_NP, Busy, Exist, XDevice, NoDevice, NotDirectory, IsDirectory, Invalid, NFile, MFile, NoTty, TextBusy_NP, FileBig_NP, NoSpace, SeekPipe, ReadOnlyFileSystem, MaxLink, Pipe, Domain_NP, Range, WouldBlock_NP, InProgress_NP, Already_NP, NotSocket_NP, DestinationAddressRequired_NP, MessageSize_NP, ProtocolType_NP, NoProtocolOption_NP, ProtocolNotSupported_NP, SocketNotSupported_NP, OperationNotSupported_NP, ProtocolFamilyNotSupported, AddressFamilyNotSupported_NP, AddressInUse_NP, AddressNotAvailable_NP, NetworkDown_NP, NetworkUnreachable_NP, NetworkReset_NP, ConnectionAborted_NP, ConnectionReset_NP, NoBuffers_NP, IsConnected_NP, NotConnected_NP, Shutdown_NP, TooManyReferences_NP, TimedOut_NP, ConnectionRefused_NP, Loop_NP, NameTooLong, HostDown_NP, HostUnreachable_NP, NotEmpty, ProcessLimit_NP, Users_NP, DiskQuota_NP, Stale_NP, Remote_NP, NoMessage_NP, IdentifierRemoved_NP, Align_NP, NoLock, NoSystem}; (* Ultrix 3.1 note: ENOSYS is not defined in errno.h *) EXCEPTION Error(EC); PROCEDURE ErrorMessage(ec: EC): TEXT RAISES {}; (* Returns an error message relating to ec in the appropriate language given the value of the LANG environment variable. *) (* Types for use with access and stat *) TYPE FileType = {Pipe, FIFO, Character, bad3_NP, Directory, bad5_NP, Block, bad7_NP, Regular, bad9_NP, Link_NP, bad11_NP, Socket_NP, bad13_NP, bad14_NP, bad15_NP}; AccessMode = {Read, Write, Execute}; AccessModes = SET OF AccessMode; AccessClass = {Others, Group, Owner}; AccessRights = ARRAY AccessClass OF BITS 3 FOR AccessModes; FileAttribute = {SaveTextAfterUse_NP, SetGroupID, SetUserID}; FileAttributes = SET OF FileAttribute; FileMode = RECORD access: BITS 9 FOR AccessRights; attributes: BITS 3 FOR FileAttributes; type: BITS 4 FOR FileType; END; (* Types for use with the seek *) TYPE SeekCommand = {Set, Current, End}; (* Types for file locking. *) TYPE FileLockOp = {ReadLock, Unlock, WriteLock}; FileLock = RECORD type: BITS 16 FOR FileLockOp; whence: BITS 16 FOR SeekCommand; start: Offset_T; length: Offset_T; pid: PID_T; END; FileLockMode_NP = {Shared, Exclusive, NoBlock, Unlock}; FileLockModes_NP = SET OF FileLockMode_NP; TYPE OpenFileMode = {Read, Write, NoDelay_NP, Append, Mark_NP, Defer_NP, Async_NP, SharedLock_NP, ExclusiveLock_NP, Create, Truncate, Exclusive, BlockInUse_NP, SetInUse_NP, NonBlock, Termio_NP, NoControlTty}; OpenFileModes = SET OF OpenFileMode; TYPE FileStatus = RECORD dev: Device_T; inode: INode_T; mode: BITS 16 FOR FileMode; nLink: BITS 16 FOR NLink_T; userID: BITS 16 FOR UserID_T; groupID: BITS 16 FOR GroupID_T; rdev_NP: Device_T; size: Offset_T; atime: Time.T; mtime: Time.T; ctime: Time.T; blockSize_NP: INTEGER; blocks_NP: INTEGER; generationNumber_NP: INTEGER; spare4_NP: INTEGER; END; TYPE File = OBJECT METHODS close() RAISES {Error}; (* Some implementations may automatically close a file if that File is garbage collected. *) read(VAR (* out *) buf: ARRAY OF CHAR): CARDINAL RAISES {Error, Alerted}; write(READONLY buf: ARRAY OF CHAR): CARDINAL RAISES {Error, Alerted}; dup(): File RAISES {Error}; getFileMode(): OpenFileModes RAISES {Error}; setFileMode(flags: OpenFileModes) RAISES {Error}; getLock(VAR (* in/out *) flock: FileLock) RAISES {Error}; setLock(READONLY flock: FileLock) RAISES {Error}; setLockWait(READONLY flock: FileLock) RAISES {Error, Alerted}; seek(offset: Offset_T; whence: SeekCommand := SeekCommand.Set ): Offset_T RAISES {Error}; getConfigurationParameter( name: PathnameConfigurationParameter): [-1..LAST(CARDINAL)] RAISES {Error}; (* Returns -1 if there is no limit for the given configuration parameter, otherwise it returns the limit. *) status(VAR (* out *) buf: FileStatus) RAISES {Error}; setOwner_NP(owner: PID_T) RAISES {Error}; getOwner_NP(): PID_T RAISES {Error}; setSynchronous_NP(state: BOOLEAN): BOOLEAN RAISES {Error}; lock_NP(lockMode: FileLockModes_NP) RAISES {Error, Alerted}; changeMode_NP(mode: FileMode) RAISES {Error}; changeOwner_NP(owner: UserID_T; group: GroupID_T) RAISES {Error}; sync_NP() RAISES {Error}; truncate_NP(length: Offset_T := 0) RAISES {Error}; ioctl_NP(operation: INTEGER; VAR (* in/out *) operand: ARRAY OF CHAR) RAISES {Error, Alerted} END; FileImpl <: File; (* FileImpl is the kind of file that is produced by this interface. *) PROCEDURE Pipe(VAR (* out *) readFile, writeFile: FileImpl) RAISES {Error}; (* Files and Directories *) TYPE DirEnumerator = Rd.T OBJECT METHODS next(): BOOLEAN RAISES {Error}; (* Calling next resets the enumerator to read the next directory entry. Returns TRUE if there is an entry following the current entry, FALSE otherwise. *) rewind() RAISES {Error}; (* Resets the reader to be reading the first directory entry again. *) inode_NP(): INode_T RAISES {Error}; (* Returns the inode of the current directory entry. *) END; PROCEDURE OpenDir(dirName: TEXT): DirEnumerator RAISES {Error}; (* Returns a reader that reads the first entry in the directory. Closing this reader terminates the entire enumeration. *) PROCEDURE ChangeDir(dirName: TEXT) RAISES {Error}; PROCEDURE GetPath(path: TEXT; dir: TEXT := NIL): TEXT RAISES {Error}; (* Returns an absolute path name for the given path relative to dir if it is given. If dir is not given, the path is evaluated relative to the working directory. dir must be the absolute path name of a directory. This procedure has no effect on the process's working directory. If the file is has more than one link its absolute path name may not be unique. Replaces getcwd from the C binding. *) CONST DefaultCreateAccess = AccessRights{ AccessModes{AccessMode.Read, AccessMode.Write}, AccessModes{AccessMode.Read, AccessMode.Write}, AccessModes{AccessMode.Read, AccessMode.Write}}; PROCEDURE Open(path: TEXT; flags: OpenFileModes; mode: FileMode := FileMode {access := DefaultCreateAccess, attributes := FileAttributes{}, type := FileType.Regular} ): FileImpl RAISES {Error, Alerted}; PROCEDURE Create(path: TEXT; mode: FileMode := FileMode {access := DefaultCreateAccess, attributes := FileAttributes{}, type := FileType.Regular} ): FileImpl RAISES {Error}; PROCEDURE SetUMask(mask: AccessRights): AccessRights RAISES {}; (* These rights are subtracted from any rights presented to Open, Create, MakeDir or MakeFIFO. *) PROCEDURE Link(path1, path2: TEXT) RAISES {Error}; CONST DefaultMakeDirAccess = AccessRights{ AccessModes{AccessMode.Read, AccessMode.Write, AccessMode.Execute}, AccessModes{AccessMode.Read, AccessMode.Write, AccessMode.Execute}, AccessModes{AccessMode.Read, AccessMode.Write, AccessMode.Execute}}; PROCEDURE MakeDir(path: TEXT; mode: FileMode := FileMode {access := DefaultMakeDirAccess, attributes := FileAttributes{}, type := FileType.Directory} ) RAISES {Error}; PROCEDURE MakeFIFO(path: TEXT; mode: FileMode := FileMode {access := DefaultCreateAccess, attributes := FileAttributes{}, type := FileType.FIFO} ) RAISES {Error}; PROCEDURE Unlink(path: TEXT) RAISES {Error}; PROCEDURE RemoveDir(path: TEXT) RAISES {Error}; PROCEDURE Rename(old, new: TEXT) RAISES {Error}; PROCEDURE Status(path: TEXT; VAR (* out *) status: FileStatus; follow_NP: BOOLEAN := TRUE) RAISES {Error}; (* If follow_NP is FALSE this call will return information about a symbolic link. *) PROCEDURE Access(path: TEXT; amode: AccessModes) RAISES {Error}; (* To check only for existence only pass in AccessType{}. *) PROCEDURE ChangeMode(path: TEXT; mode: FileMode) RAISES {Error}; (* mode.type is ignored. *) PROCEDURE ChangeOwner(path: TEXT; owner: UserID_T; group: GroupID_T) RAISES {Error}; TYPE UpdateTimeBuf = RECORD atime: Time.T; mtime: Time.T; END; PROCEDURE UpdateTime(path: TEXT; times: UpdateTimeBuf) RAISES {Error}; PROCEDURE GetConfigurationParameter( path: TEXT; name: PathnameConfigurationParameter ): [-1..LAST(CARDINAL)] RAISES {Error}; (* Returns -1 if this parameter value is not applicable for the given file. *) (* Process Environment *) PROCEDURE GetPID(): PID_T RAISES {}; PROCEDURE GetParentPID(): PID_T RAISES {}; PROCEDURE GetUserID(): UserID_T RAISES {}; PROCEDURE GetEffectiveUserID(): UserID_T RAISES {}; PROCEDURE GetGroupID(): GroupID_T RAISES {}; PROCEDURE GetEffectiveGroupID(): GroupID_T RAISES {}; PROCEDURE SetUserID(user: UserID_T) RAISES {Error}; PROCEDURE SetGroupID(groupID: GroupID_T) RAISES {Error}; PROCEDURE GetGroups(): REF ARRAY OF GroupID_T RAISES {}; PROCEDURE GetLogin(): TEXT RAISES {Error}; PROCEDURE CurrentUser(): TEXT RAISES {Error}; PROCEDURE GetProcessGroup(): PID_T RAISES {}; PROCEDURE SetSessionID(): PID_T RAISES {Error}; TYPE UName = RECORD systemName: TEXT; nodeName: TEXT; release: TEXT; version: TEXT; machine: TEXT; END; PROCEDURE GetUName(): UName RAISES {Error}; TYPE Times = RECORD userTime: Clock_T; systemTime: Clock_T; childUserTime: Clock_T; childSystemTime: Clock_T; elapsedRealTime: Clock_T; END; PROCEDURE GetTimes(): Times RAISES {Error}; PROCEDURE CurrentTerminal(): TEXT RAISES {Error}; PROCEDURE GetSystemConfigurationParameter( name: SystemConfigurationParameter ): [-1..LAST(CARDINAL)] RAISES {Error}; (* Synchronous hardware detected errors. *) (* Hardware detected faults are always seen as exceptions to the programmer. Note that these exceptions may have been the result of a language detected runtime error, so catching one of these exceptions may mask such an error. *) TYPE IllegalInstructionArgument_NP = {ReservedAddress, PrivilegedInstruction, ReservedOperand}; EXCEPTION IllegalInstruction(IllegalInstructionArgument_NP); TYPE FloatingPointExceptionArgument_NP = { IntegerOverflow, IntegerDivideByZero, FloatingOverflow, FloatingDivideByZero, FloatingUnderflow, DecimalOverflow, SubscriptOutOfRange, FloatingOverflowFault, FloatingDivideByZeroFault, FloatingUnderflowFault}; EXCEPTION FloatingPointException(FloatingPointExceptionArgument_NP); TYPE SegmentationViolationArgument_NP = ADDRESS; EXCEPTION SegmentationViolation(SegmentationViolationArgument_NP); (* Signals *) TYPE SignalOrNone = {Null, HangUp_NP, Interactive, Quit, IllegalInstruction, Trap_NP, Abort, EMT_NP, FloatingPoint, Kill, Bus_NP, SegmentViolation, BadSystemCall_NP, Pipe, Alarm, Terminate, Urgent_NP, Stop, TerminalStop, Continue, Child, TtyIn, TtyOut, IOReady_NP, ExceededCPUTime_NP, ExceededFileSize_NP, VirtualAlarm_NP, ProfilingAlarm_NP, WindowChange_NP, bad29_NP, User1, User2}; Signal = [SignalOrNone.HangUp_NP .. SignalOrNone.User2]; (* Signals are split into four categories by the Modula 3 POSIX. Hardware detected errors are converted into exceptions in the appropriate thread. These signals should not be sent or manipulated by the programmer. These signals include: IllegalInstruction, Trap_NP, SigAbort, EMT_NP, FloatingPoint, Bus_NP, SegmentViolation, BadSystemCall_NP. Asynchronous signals used for multiplexing a single thread are reserved for use inside the Modula 3 runtime and should not be sent or manipulated by clients. This signals include: Alarm, Urgent_NP, Child, IOReady_NP, VirtualAlarm_NP, ProfilingAlarm_NP. Clients should instead use separate threads making blocking calls. Synchronous serious system detected error exceptions may be either ignored or defaulted by the client. Ignoring them causes the appropriate Error exception to be raised by the thread making the serious error. Defaulting the signal causes the normal default action. These signals should not be sent or set handled state. These signals include: Pipe, TtyIn, TtyOut, ExceededFileSize_NP. The related error codes that are used if the error occurs and the signal is ignored are EC.Pipe, EC.Again, EC.Again and EC.DiskQuota. The remaining signals may be sent to other processes and, except for Signal.Kill and Signal.Stop, they may be either defaulted, ignored or handled by the client. Signals that are handled are logically blocked at all times. They are handled by creating a new thread that blocks waiting for the signal to become pending. See WaitForSignal below. *) TYPE SignalAction = {Default, Ignore, Handle}; PROCEDURE SetSignalAction(signal: Signal; action: SignalAction): SignalAction RAISES {Error}; (* Note that some implementations may restrict this call so that it can only be made in the initial thread. Some implementations may restrict the use of this call as outlined above. Error(EC.Invalid) is raised if the action is inappropriate for the signal. *) TYPE Signals = SET OF Signal; PROCEDURE WaitForSignal(allowed: Signals): Signal RAISES {Error, Alerted}; (* Waits for one of the given signals to become pending. When one does become pending, it is atomically cleared and its identity is returned. *) PROCEDURE GetPending(): Signals RAISES {}; (* Returns the set of pending signals. *) (* Process operations. *) TYPE ProcessWaitOption = {NoHang, Untraced}; ProcessWaitOptions = SET OF ProcessWaitOption; TYPE TerminationReason = {Stopped, Killed, Exited}; ExitStatus = [0 .. 255]; TerminationStatus = RECORD reason: TerminationReason; signal: Signal; (* if reason is Stopped or Killed *) coreDump: BOOLEAN; (* if reason is Killed *) retCode: ExitStatus; (* if reason is Exited *) END; TYPE Process = OBJECT METHODS wait(options: ProcessWaitOptions := ProcessWaitOptions {} ): TerminationStatus RAISES {Error, Alerted}; kill(signal: Signal) RAISES {Error}; setProcessGroup(pgrp: PID_T) RAISES {Error}; getPID(): PID_T RAISES {Error} END; ProcessImpl <: Process; PROCEDURE OpenProcess(pid: PID_T := 0): ProcessImpl RAISES {Error}; (* OpenProcess(0) returns the calling process. Raises Error(EC.Search) if pid is not found. *) (* Process creation and execution. *) (* To allow easy use with threads, we define a new set of procedures for process creation. By default, a new process inherits everything as it would following a POSIX fork and exec except it inherits no file descriptors. Changes made to the template change those defaults. *) TYPE Relationship = {Orphan, Child, Self}; (* Starting a process with relationship set to Self, replaces the calling process's address space and process state with that specified by the process template. All the threads in the parent process are killed (as in Signal.Kill), and the new process is started with a single thread of control. *) TYPE ProcessTemplate = OBJECT METHODS (* Any of these methods may raise EC.Invalid if the process template has been closed or has already been used to start a process. *) setDescriptor(d: CARDINAL; f: File) RAISES {Error}; (* Causes the child to be given the given file as descriptor d. The file may still be used by the parent or it may be closed with no effect on the child. *) setUMask(umask: FileMode) RAISES {}; setSessionID() RAISES {Error}; setWD(path: TEXT) RAISES {Error}; setControlTerminal(f: File; setGroup: BOOLEAN := TRUE) RAISES {Error}; (* If setGroup is TRUE the process group of the terminal is set to that of the new process once that child is created *) unsetControlTerminal() RAISES {Error}; (* The control terminal of the child is unset. *) setPGRP() RAISES {Error}; (* Sets the process group of the new process to its be the same as its PID. *) setUserID(userID: UserID_T) RAISES {Error}; setGroupID(groupID: GroupID_T) RAISES {Error}; setSignalAction( signal: Signal; state: [SignalAction.Default..SignalAction.Ignore] ) RAISES {Error}; (* There are no restrictions on the signal action on a child process. *) setSignalMask(signalMask: Signals) RAISES {Error}; start( path: TEXT; READONLY argv: ARRAY OF TEXT; relationship: Relationship := Relationship.Child; environment: REF ARRAY OF TEXT := NIL ): Process RAISES {Error}; (* Starts a process with data as set in this template. If environment is NIL, the child inherits the environment of this process. Each text in environment is of the form =. Returns the process ID of the created process. If relationship = Orphan then the new process's parent process is set to 0. Once a template has been used to successfully start one process, it may not be used again. There is no need to call close for such a template. The errors on this operation are the sum of those from fork and exec. *) close() RAISES {Error}; (* Used to deallocate all the resources associated with a template that has not been used to start a process. *) END; ProcessTemplateImpl <: ProcessTemplate; PROCEDURE NewTemplate(): ProcessTemplateImpl RAISES {Error}; (* System Databases *) TYPE GroupInfo = RECORD name: TEXT; groupID: GroupID_T; memberNames: REF ARRAY OF TEXT; END; PROCEDURE GetGroupByID(groupID: GroupID_T; VAR (* out *) info: GroupInfo) RAISES {Error}; PROCEDURE GetGroupByName(name: TEXT; VAR (* out *) info: GroupInfo) RAISES {Error}; TYPE UserInfo = RECORD name: TEXT; userID: UserID_T; groupID: GroupID_T; dir: TEXT; shell: TEXT; END; PROCEDURE GetUserByID(userID: UserID_T; VAR (* out *) info: UserInfo) RAISES {Error}; PROCEDURE GetUserByName(name: TEXT; VAR (* out *) info: UserInfo) RAISES {Error}; END POS. (* File: POSPath.i3 *) (* Last modified on Thu Apr 19 13:13:55 PDT 1990 by swart *) (* modified on Fri Jan 20 15:27:49 PST 1989 by glassman *) (* modified on Wed Apr 22 17:34:04 1987 by roberts *) (* modified on Tue Nov 25 11:48:02 1986 by brooks *) INTERFACE POSPath; IMPORT Text, Rd, POS; PROCEDURE Root(path: TEXT) : TEXT RAISES {}; (* Returns the root part of the path and is equivalent *) (* to the :r substitution in the C shell. *) PROCEDURE Extension(path: TEXT) : TEXT RAISES {}; (* Returns the extension part of the path and is *) (* equivalent to the :e substitution in the C shell. *) PROCEDURE Head(path: TEXT) : TEXT RAISES {}; (* Returns the head part of the path and is equivalent *) (* to the :h substitution in the C shell. *) PROCEDURE Tail(path: TEXT) : TEXT RAISES {}; (* Returns the tail part of the path and is equivalent *) (* to the :t substitution in the C shell. *) PROCEDURE DefaultExtension(path, ext: TEXT) : TEXT RAISES {}; (* Adds an extension to a path if none already *) (* exists. Alternatively, if the extension field begins *) (* with a *, any old extension in the first path is *) (* replaced with the given extension. *) PROCEDURE ExpandTilde(path: TEXT) : TEXT RAISES {POS.Error}; (* Expands the ~ character at the beginning of a file *) (* name to be the appropriate directory path. For ~/ *) (* this is taken from the HOME environment variable; *) (* ~user/ is converted by looking up the users home directory. *) TYPE FilePredicate = PROCEDURE(name: TEXT) : BOOLEAN RAISES {}; PROCEDURE SearchPath( searchPath, path: TEXT; pred: FilePredicate := NIL) : TEXT RAISES {POS.Error}; (* Most clients will not specify a filepred argument *) (* and will use this simply to find the first file *) (* that exists along a given search path. More *) (* generally, SearchPath returns a path to the *) (* first instance of path in the list of *) (* directories specified by the search path for which the *) (* filepred returns TRUE. If none exist, SearchPath *) (* returns NIL. *) END POSPath. (* File: POSTerminal.i3 *) (* Last modified on Thu Apr 19 13:41:30 PDT 1990 by swart *) INTERFACE POSTerminal; FROM POS IMPORT FileImpl, Error, PID_T; FROM Thread IMPORT Alerted; (* General Terminal Interface *) TYPE ControlCharacters = {Interrupt, Quit, Erase, Kill, EOF, EOL, EOL2_NP, Switch_NP, Minimum, Time, Start, Stop, Suspend, DelayedSuspend_NP, Reprint_NP, Flush_NP, WordErase_NP, LiteralNext_NP, Quote_NP}; CONST (* The value of this constant is Ultrix dependent *) ControlDisable: CHAR = '\000'; TYPE InputFlags = {IgnoreBreak, (* Ignore break condition *) BreakInterrupt, (* Signal interupt on break *) IgnoreParity, (* Ignore characters with parity errors *) MarkParity, (* Mark Parity errors *) InputParity, (* Enable input parity check *) Strip, (* Strip character to 7 bits *) NL2CR, (* Map NL to CR on input *) IgnoreCR, (* Ignore CR *) CR2NL, (* Map CR to NL on input *) upperToLower_NP, (* Map upper-case to lower-case on input *) XOn, (* Enable start/stop output control *) XAny_NP, (* Enable any character to restart output *) XOff, (* Enable start/stop input control *) bad13_NP, bad14_NP, bad15_NP, PendingIn_NP, (* Retype pending input at next read or input. *) CanonicalBreak_NP (* Limited canonical processing *)}; InputMode = SET OF InputFlags; OutputFlags = {PostProcess, LowerToUpper_NP, NL2CR_NP, CR2NL_NP, NoCR_NP, NLMeansCR_NP, FillDelay_NP, FillIsDEL_NP}; SetOfOutputFlags = SET OF OutputFlags; OutputDelays_NP = RECORD newline: BITS 1 FOR [0 .. 1]; verticalTab: BITS 1 FOR [0 .. 1]; horizontalTab: BITS 2 FOR [0 .. 3]; carriageReturn: BITS 2 FOR [0 .. 3]; formFeed: BITS 1 FOR [0 .. 1]; backspace: BITS 1 FOR [0 .. 1]; END; ExtraOutputFlags_NP = {TildeConvert, FlushOutput, literalOut, NLType2_NP}; SetOfExtraOutputFlags_NP = SET OF ExtraOutputFlags_NP; OutputMode = RECORD mode: BITS 8 FOR SetOfOutputFlags; delays_NP: BITS 8 FOR OutputDelays_NP; extraMode_NP: BITS 16 FOR SetOfExtraOutputFlags_NP; END; BaudRate_NP = {B0 (* Hang up *), B50, B75, B110, B134, B150, B200, B300, B600, B1200, B1800, B2400, B4800, B9600, B19200, B38400}; CharacterSize = {CS5, CS6, CS7, CS8}; ControlFlags = {StopBits, (* Send two stop bits, else one. *) Read, (* Enable receiver *) ParityEnable, (* Parity enable *) ParityOdd, (* Odd parity, else even *) HangUpClose, (* Hang up on last close *) Local, (* Ignore modem status lines *) LowBlock_NP (* ??? *) }; SetOfControlFlags = SET OF ControlFlags; ControlMode = RECORD inputRate_NP: BITS 4 FOR BaudRate_NP; csize: BITS 2 FOR CharacterSize; mode: BITS 10 FOR SetOfControlFlags; outputRate_NP: BITS 4 FOR BaudRate_NP; autoflow_NP: BITS 1 FOR BOOLEAN; END; LocalFlags = {Signal, (* enable signals *) InputCanonical, (* Canonical input *) XCase_NP, (* Canonical upper/lower presentation *) Echo, (* Enable echo *) EchoErase, (* Echo ERASE as an error correcting backspace *) EchoKill, (* Echo KILL *) EchoNL, (* Echo NL *) NoFlush, (* Disable flush after interrupt *) bad8_NP, bad9_NP, bad10_NP, bad12_NP, bad13_NP, bad14_NP, bad15_NP, ControlEcho_NP, (* Echo input control chars as ^X *) PrintErase_NP, (* Hardcopy terminal erase mode using \c *) CRTBackSpace_NP, (* Backspace on erase *) CRTErase_NP, (* CRT Erase mode *) CRTKill_NP, (* BS-space-BS erase entire line on kill *) Raw_NP, (* Berkeley non-canonical I/O *) ToStop, (* Send SIGTTOU for bg output *) EnableExtension (* Enable local special characters *) }; LocalMode = SET OF LocalFlags; LineDiscipline_NP = {OldTty, (* old, v7 std tty driver *) NetLine, (* line discip for berk net *) NewTty, (* new tty discipline *) Tablet, (* hitachi tablet discipline *) NewTablet, (* gtco tablet discipline *) HalfCooked, (* half cooked discipline *) TermIO, (* termIO line discipline *) SLIP (* BSD Serial Line IP *) }; TermIOS = RECORD inputMode: BITS 32 FOR InputMode; outputMode: BITS 32 FOR OutputMode; controlMode: BITS 32 FOR ControlMode; localMode: BITS 32 FOR LocalMode; cc: ARRAY ControlCharacters OF CHAR; lineDiscipline_NP: BITS 8 FOR LineDiscipline_NP; END; TYPE SetAction = {Now, AfterDrain, AfterFlush}; Queue = SET OF {Input, Output}; FlowAction = {OutputOn, OutputOff, InputOff, InputOn}; (* The isatty operation of the C binding is replaced by a TYPECASE. *) TYPE T = FileImpl OBJECT METHODS getTerminalAttributes(VAR (* out *) termios: TermIOS) RAISES {Error}; setTerminalAttributes(READONLY termios: TermIOS; action: SetAction := SetAction.Now) RAISES {Error, Alerted}; sendBreak(duration: CARDINAL := 0) RAISES {Error}; drain() RAISES {Error, Alerted}; flush(queue: Queue) RAISES {Error}; flow(action: FlowAction) RAISES {Error}; setProcessGroup(group: PID_T) RAISES {Error}; getProcessGroup(): PID_T RAISES {Error}; ttyName(): TEXT RAISES {Error}; END; TImpl <: T; END POSTerminal. ------------------------------------------------------------------------------ Date: Mon, 23 Apr 90 11:58:51 PDT From: Eric Muller Subject: Multithreaded m3 calls C Bob Ayers (ayers@src.dec.com) asked me about concurrent calls to X from SRC Modula-3. I did not experiment with concurrent calls to C, but here is what I can guess on the subject. SRC Modula-3 uses SIGPROF and SIGALARM to implement threads. SIGPROF is used to switch to another thread, while SIGALARM is used when all the threads are paused. sigvec(2) is called for SIGPROF only when a thread is forked. sigvec is called for SIGALARM when a thread (including the starting one) calls Time.Pause. I think that the only place in the runtime/library that calls Time.Pause is terminal input, on some routines such as CharsReady. Of course, code called from multiple threads has to be reentrant. Not all the standard C code is so, nor X itself. I think that I have seen somewhere that this problem will be addressed in X11R5. You may want to ask some Xperts to sort out the details. A case of non-reentrance introduced by cc: if a function returns a struct, cc allocates some static space for the result on the callee side, which returns a pointer to that area. gcc can behave that way or have the caller allocate some auto space (ie. on the stack) and pass an extra argument to the callee. Modula-3 always does that for Modula-3 routines. Another problem: to increase the compatibility of SRC Modula-3 with C, procedure arguments are handled as follows: for global procedures and local procedures that don't use stack-allocated objects (either in their own scope or an enclosing, non-global scope), a pointer to the procedure is passed; for local procedures that need to access the stack, a pointer to a "closure" is passed (two words, the first points to the procedure itself, the second to the frame). Right now, we do issue a warning when the second kind of procedure is passed to C code. I hope this helps. Eric Muller. ------------------------------------------------------------------------------ Date: Thu, 26 Apr 90 09:45:27 +0100 From: mjj%computer-lab.cambridge.ac.uk@NSFnet-Relay.AC.UK Subject: pragma warnings I dont think the compiler should warn about unrecognised pragmas, unless asked explicitly. There are many uses of pragmas which are outside the domain of the compiler. I know I can set -w3 but that turns off all warnings. Mick ------------------------------------------------------------------------------ Date: Thu, 26 Apr 90 17:39:40 PDT From: Eric Muller Subject: Re: pragma warnings > I dont think the compiler should warn about unrecognised pragmas, unless > asked explicitly. There are many uses of pragmas which are outside the > domain of the compiler. I know I can set -w3 but that turns off all > warnings. On the contrary, I think it is a very good idea. I'd rather know when I mispelled EXTERNAL for example. eric. ------------------------------------------------------------------------------ Date: Fri, 27 Apr 90 08:35:00 +0100 From: mjj%computer-lab.cambridge.ac.uk@NSFnet-Relay.AC.UK Subject: Re: Ultrix/Unix >This subject has already been discussed in the SRC Modula-3 mailing >list (also duplicated in the srcext.m3 bboard at SRC). My view is: >we don't have the include files and man pages for any other os than >Ultrix 3.1 and we cannot guess them. Well we only had the man pages for SunOS and BSD. The point is that the vendor Unix interfaces, especially the BSD derivatives, are very similar. We ported our code to several Unixes without problem, except in the more obscure areas like sockets. I admit that the purity of a single-vendor interface is attractiv e but it doesnt help people who want to write Unix-based code which is in fact portable across a wide range of vendors. Code duplication is a bad idea, and it seems to me that this is a classic area for conditional compilation. I cant see the C/C++ folks signing on to vendor specific header files. The aim of the "standard" interfaces is exactly to produce something called "Unix"; it just gets a different name like POSIX for polital reasons. And I think its naive to assume that these "evolved" Unixes will not also become vendor specific! Mick ------------------------------------------------------------------------------ Date: 27 Apr 1990 1010-PDT (Friday) From: Subject: Re: pragma warnings > > I dont think the compiler should warn about unrecognised pragmas, unless > > asked explicitly. There are many uses of pragmas which are outside the > > domain of the compiler. I know I can set -w3 but that turns off all > > warnings. > > On the contrary, I think it is a very good idea. I'd rather know when > I mispelled EXTERNAL for example. > > eric. Given that we make use of pragmas in a quite different way in Larch/Modula-3, I would be happier if a warning or not was an option that one could set. Unless, of course, you think it's reasonable to extend the compiler to recognise all the LM3 constructs 8-). Kevin ------------------------------------------------------------------------------ Date: Fri, 27 Apr 90 12:37:20 PDT From: Eric Muller Subject: Re: pragma warnings > Given that we make use of pragmas in a quite different way in > Larch/Modula-3, I would be happier if a warning or not was an option > that one could set. The -w option can be set to limit the set of warnings you get. You can argue with the classification of warnings, and we will take everybody's opinion into account. Maybe we could have a <*PRAGMAS tool name-list*> pragma that lists the pragmas that are to be recognized by one tool and ignored by others. A default table could be specified when the system is constructed, and individual programs could override/extend it. eric. ------------------------------------------------------------------------------ Date: Fri, 27 Apr 90 12:50:39 PDT From: Eric Muller Subject: Re: TextF.i3 > We found it useful to include a 'New' function in TextF to create a TEXT whic h > could be filled in directly by clients of the representation. It can hide > gory details like the space for the null. E.g. > <*INLINE*> PROCEDURE New(n: CARDINAL): Text.T RAISES {}; > (* create a new text capable of holding 'n' characters. Note that its actual > length as an array will be 'n+1' because of the null termination. > The characters at positions '0..n-1' are left undefined. The character at > position 'n' is set to null *) > Mick Done in 1.5. eric. ------------------------------------------------------------------------------ Date: Fri, 27 Apr 90 13:11:11 PDT From: Jacobi.pa@Xerox.COM Subject: Re: pragma warnings "we will take everybody's opinion into account" My preference would be if the programmer could specify whether it is ok to not understand a particular pragma or whether this would be a failure. E.g: -All pragma for optimization may be safely ignored by a compiler -Pragma like external languages interface hints could prevent the program to run if not understood by a compiler. There are pragma where A particular compiler knows what the pragma means [Even if it might decide to ignore it] A particular compiler does not know what to do For the latter case the programmer knows whether ignoring the pragma is ok or whether ignoring the pragma would cause a non-working binary to be produced. This knowledge must somehow be told to the compiler so a message might be generated. Always doing a warning is bad because this will simply teach people to ignore warnings; they will then miss the rare case whaen a warning would be important. Christian ------------------------------------------------------------------------------ Date: Fri, 27 Apr 90 18:18:28 EDT From: moss%ibis@cs.umass.edu (Eliot Moss) Subject: Pragmas and warnings Well, I have been observing the discusssion for a while now, and, based upon a recent note from Christian, I would suggest that there are indeed two kinds of pragmas, that we might want to have both of them, and they need to be distinguished. I would describe them slightly differently, though: o Pragmas that have no effect on semantics, under any compiler; these can be ignored safely by any compiler. o Pragmas that have a semantic effect, particular to a given compiler or collection of compilers. These should cause warnings if not recognized. The designers of the language have taken a more "purist" position is the past and have indicated reluctance to allow/add the second kind of pragma. While such things do make programs less portable, they at least make the non-portability obvious, and they do allow for modest extensions and features within the framework of the language, so I favor them. Note that pragma EXTERNAL indeed falls into this category, as noted by Christian, since it can affect how something is compiled, and therefore whether or not a binary works. I have a number of ideas concerning modest extensions that would most easily be handled by pragmas, with some of them affecting semantics and some not. Here is an example of one that does affect semantics a bit: a pragma to indicate that a particular method may not be overridden in the NEW declaration. That fact, combined with knowledge of the type hierarchy in a program, would allow some method calls to be inlined without analysis of all code in the system. Strenthening the pragma (or adding a similar one) that does not allow it to have overrides in subclasses either would make it even easier to inline some method calls. If the user later wanted to override, they would have to change the pragma and recompile (this would be a property of a type as declared in an interface). In this way we can relatively cleanly add an interesting tweak on the basic facilities of the language. The example I gave is actually kind of subtle: it could safely be ignored by compilers that did not understand it, but it has a semantic effect in those that do. However, its effect is to make certain program illegal rather than to change what any programs mean. Still, it would result in different behavior under different compilers. So if we broadened the classification to those pragmas that have no effect on the legality or interpretation of programs versus those that do, then my example pragma would be a legality affecting one and would fall into the "potentially unsafe to ignore" category. Anyway! I support a distinction, though I am not at all sure about the best way to make the distinction. We could have different syntax (i.e., something other than <* *> surrounding one kind of pragma) or perhaps do it via the forms of the contents of the pragmas. Here is one suggestion off the top of my head: if the first non-white-space character in the pragma is an exclamation point, then the pragma is one that has a semantic/behavioral effect and should cause a warning to be emitted if it is not understood by the compiler at hand. A suggested pragma naming scheme should probably be devised, too, so that one compiler's pragmas are not misinterpreted by another compiler. I am not at all sure about how *best* to do that. One thing that occurs to me is to have a list of "approved" or "standard" pragmas, with accepted meanings, but no requirement that they be implemented. INLINE, EXTERNAL, and such things as listing control seem to fall easily into that category. Enabling/disabling various optimizations could be somewhat standardized, too, I expect. Compiler specific pragmas would be prefaced with something related to that compiler. For example, the pragma I proposed above might read <* ! gnu: no override *> or seomthing like that. If some other compiler wanted to pick it up, that's fine, and would be a time to request inclusion on the "standard pragma" list. This notion of a standard pragma list is intended to keep the language definition *per se* small, since it would refer to the existence of the list, and might give the current one in an appendix. Reactions? Eliot J. Eliot B. Moss, Assistant Professor Department of Computer and Information Science Lederle Graduate Research Center University of Massachusetts Amherst, MA 01003 (413) 545-4206; Moss@cs.umass.edu ------------------------------------------------------------------------------ Date: Fri, 27 Apr 90 20:07:14 CDT From: rachel!johnh@gargoyle.uchicago.edu (John (Doc) Hayward) Subject: Comments on r.f short hand for r^.f In modula-3 =========== TYPE Stu_Rec = RECORD Id : INTEGER; GPA : REAL; Sex : CHAR END; Stu_Pointer = REF Stu_Rec; VAR Stu1, Stu2 : Stu_Pointer; Stu3, Stu4 : Stu_Rec; BEGIN Stu1 := NEW(Stu_Rec); Stu2 := NEW(Stu_Rec); Stu1.Id := 1234; Stu3.Id := 1234; Stu1.GPA := 3.5; Stu3.GPA := 3.5; Stu1.Sex := "F"; Stu3.Sex := "F"; (* here is what one of my student did *) Stu2 := Stu1; (* here is what he wanted *) Stu2^ := Stu1^; Stu4 := Stu3; ======== When I first read that if r is a reference to a record that r.f is a shorthand for r^.f, I had two conflicting feelings 1) this is a convient notational device and 2) there is probably some place where this will cause a problem. It seems that people will use this shorthand for most references to components of dynamicaly created data stuctures (a lot fewer ^s). However to reference the entire data structure the ^ is still required (because without the ^ it would refer to the pointer itself). I think that this lack of uniformity will cause some problems both to readers and writers of code (Assignment to componets of both dynamic and static data structures can look the same while assigment to the entire dynamic data structrue is required to be different than static structures). This problem needs to be pointed out to new users of the language. johnh... -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- = UUCP: johnh@wheaton.uucp telephone: (708) 260-3871 (office) Mail: John Hayward Math/Computer Science Dept. Wheaton College Wheaton Il 60187 Act justly, love mercy and walk humbly with your God. Micah 6:8b ------------------------------------------------------------------------------ Date: 27 Apr 1990 1827-PDT (Friday) From: Subject: Re: Pragmas and warnings Eliot Moss suggests: ... a pragma to indicate that a particular method may not be overridden in the NEW declaration. That fact, combined with knowledge of the type hierarchy in a program, would allow some method calls to be inlined without analysis of all code in the system. Strengthening the pragma (or adding a similar one) that does not allow it to have overrides in subclasses either would make it even easier to inline some method calls. I don't think the unextended pragma would help much. Consider: TYPE T = OBJECT METHODS <*NO NEW OVERRIDE*> P() := P1 END; PROCEDURE P1(x: T); VAR x: T; ... x.P() Here we assume that the <*NO NEW OVERRIDE*> pragma prevents overriding the P method in an expression of form NEW(T, ...). Can we replace the call to X.P() with P1(x)? No, because the allocated type of x can be any subtype of T; that is, the "..." could have been: TYPE TT = T OBJECT METHODS P := P2 END; PROCEDURE P2 = ...; x := NEW(TT) Without sophisticated flow analysis, there is no way to tell that at a particular call to x.P(), the variable x has allocated type T rather than some subtype. In general, the flow analysis would have to trace the use of x to the NEW expression that allocated it, in order to infer its allocated type. But then there is no need for the <*NO NEW OVERRIDE*> pragma, since the flow analysis algorithm can inspect the relevant NEW expression and observe that the method is not overridden. Elliot also proposes an extended pragma (say, <*NO OVERRIDE*>), where the meaning of TYPE T = OBJECT METHODS <*NO OVERRIDE*> P() := P1 END; PROCEDURE P1(x: T); means that the P method can't be overridden either at allocation time or in any subtype of T. This fixes the problem with the first pragma. But with the second pragma, almost nothing is gained by using a method at all: the client should just call P1(x) instead of x.P(), since the two are everywhere equivalent. ------------------------------------------------------------------------------ Date: Fri, 27 Apr 90 22:12:15 EDT From: moss%ibis@cs.umass.edu (Eliot Moss) Subject: More on override pragma Having received Greg Nelson's response, I feel perhaps I did not make my intent clear enough (I was only trying to describe the no override pragma as an example anyway). In the weaker form of NO OVERRIDE one would need to know the full *type hierarchy* descended from a type, in addition to having the NO OVERRIDE at a NEW expression restriction. The idea is that if the method is *not* overridden in any subtype, then you *can* inline it, though you will have to undo that decision later if an overriding subtype is added. (I know this messes up totally independent compilation, but the benefits of inlining might out weigh the recompilation in a number of interesting programs; besides, nobody's would be *forced* to use this feature.) Some preliminary analysis of programs in Trellis (Owl) indicated that well over 50% of calls could be statically bound in the programs examined. If they can be statically bound, they can be inlined. The stronger form of the pragma allows you to *know* that methods will not be overridden *further down* the hierarchy. Note that either pragma could have a flavor specific to a given method or global to a whole type (though adding methods should never really be a problem). However, just because a method cannot be overridden *below* a given type does not mean that it cannot be overridden *above* that type. The implication is that when all you know is weak information (that you are high in the hierarchy) then you would not be able to do static binding or inlining, but when you know more, you could do such things. If you later change your mind and want to override in a subtype, that's fine, but you may lose some performance. In sum, it is still useful to have something be a method, but you may have confidence that you will not be changing a piece of software soon and may be willing to have these additional optimizations performed. The purpose of the pragmas is to annotate programmer intent as well as to make the optimizations easier for a compiler to perform. A further comment I have on the situation is that I believe that the method invocation overhead in Modula 3 is only slightly more than procedure call overhead, and will not discourage use of object oriented programming, *except* that more and more compilers are performing more complex global (inter-procedural) analyses to improve code quality. The hidden cost of the object oriented is loss of these optimizations because dynamic binding interferes. Truly global analysis is still *possible* for Modula-3 programs, of course, but the pragmas I have discussed make it much easier to perform some fo the interesting optimizations, and do not require global analysis on the part of the compiler. That is, it fits reasonably well with the separate compilation scheme of Modula-3. I am always welcome to suggestions for improvements, of course. Another potentially interesting thing, though I am not sure how best to work out some of the details, is to make individual fields of records or objects read-only, or read-only except to implementations of certain modules. Knowling more about what can/cannot change can alos boost optimization possibilities and provide better documentation of programmer intent. Such incremental changes could be explored with "non-ignorable" pragmas. Multiple inheritance approaches might also be explored that way. Glad to see some people are reading and thinking about messages to the list! Eliot J. Eliot B. Moss, Assistant Professor Department of Computer and Information Science Lederle Graduate Research Center University of Massachusetts Amherst, MA 01003 (413) 545-4206; Moss@cs.umass.edu ------------------------------------------------------------------------------