The word glossary is the core of Forth documentation. No program is complete without it. Until is no exception. This chapter has a complete word glossary and several smaller, specialized glossaries on specific subject areas, such as files. The main word glossary contains the word descriptions and the specific glossaries contains complete code examples.
Each glossary entry first line contains the word name and the stack comment. Short examples are included in many cases. Word pronunciation is enclosed in double quotes at the start of the description. A stack comment is enclosed in parens:
( input --- result )
The "---" represents the action of the word. Input and result can be:
( 'string --- )
This section identifies the words that are specific to the S-Engine SGML document interpreter. It is not included in this manual.
" this is a test" append_this
500 string trash ... trash read_into
" anyfile.sgm" sengine_init
The previous section describes the processing mechanisms of S-Engine. This section describes a simple, but complete S-Engine application. The file, ADDRESS.DTD, is an SGML Document Type Definition (DTD) that describes an address book document. It describes the structure of and markup (elements) that appear in the SGML instance (document) for an address document type.
The file ADDRESS.SGM is a document instance. It is a valid SGML file that can be validated using an SGML parser and ADDRESS.DTD. That level of SGML processing is beyond the scope of this manual. Here we are only interested in processing ADDRESS.SGM with S-Engine.
The example S-Engine program, ADDRESS.APP, converts the SGML file, ADDRESS.SGM, to HTML suitable for viewing on a Web Server with Mosaic or NetScape. ADDRESS.HTM is also a valid SGML file that is described by a DTD and can be validated or processed with any SGML tool.
The overall strategy of the program is to collect text for a complete address record, then process the text when and end of record tag (</entry>) executes.
The file structure is straight forward. The entire file is wrapped up by the address tags, <address> and </address>. Each address record is wrapped by the entry tags, <entry> and </entry>. This SGML design allows the wrapper tags to be used as "triggers" to initiate processing at defined points in the file.
The ADDRESS.APP code contains comments with Section numbers that correspond to the descriptions that follow.
One of the beauties of SGML is the structure defined by the element tags can be used to key processing from.
Here is the code for ADDRESS.APP:
\ Address book - S-Engine Demo \ ==================================================================== \ SECTION 1 \ ==================================================================== variable fd.out 10 constant <nl> 4096 string out.buf \ ==================================================================== \ SECTION 2 \ ==================================================================== 80 string name 32 string bphone 32 string aphone 32 string hphone 32 string fax 80 string title 80 string category 80 string company 80 string addr1 80 string addr2 80 string city 80 string st 16 string zip 4096 string note \ ==================================================================== \ SECTION 3 \ ==================================================================== : clear ( address --- ) 0 swap c! ; : -newline ( 'string --- ) dup strlen + 1- \ Calc address dup c@ <nl> = \ Compare last char to newline if 0 swap c! \ yes, trash it else drop endif ; : output.line ( ?? 'string --- ) fd.out @ fprintf ; : msg ( 'string --- ) dup strlen type ; \ ==================================================================== \ SECTION 4 \ ==================================================================== : clear.fields ( --- ) name clear bphone clear aphone clear hphone clear fax clear title clear category clear company clear addr1 clear addr2 clear city clear st clear zip clear note clear ; : setup.HTML " <HTML>\n" output.line " <HEAD><TITLE>SGML Address Book</TITLE></HEAD>\n" output.line " </HEAD>\n" output.line " <BODY>\n" output.line ; : process.name " <HR>\n" output.line name strlen 0> if ( There is a name field ) out.buf name strcpy else ( use the company name ) out.buf company strcpy company clear endif out.buf output.line ; : process.phones " <DL>\n" output.line bphone strlen 0> if bphone " <DT>Bus.: <DD>%s\n" output.line endif hphone strlen 0> if hphone " <DT>Home: <DD>%s\n" output.line endif fax strlen 0> if fax " <DT>Fax : <DD>%s\n" output.line endif aphone strlen 0> if aphone " <DT>Alt : <DD>%s\n" output.line endif " </DL>\n" output.line ; : process.the.rest " <P>\n" output.line title strlen 0> if title " %s<BR>\n" output.line endif company strlen 0> if company " %s<BR>\n" output.line endif addr1 strlen 0> if addr1 " %s<BR>\n" output.line endif addr2 strlen 0> if addr2 " %s<BR>\n" output.line endif city strlen 0> if city st zip " %s, %s %s<BR>\n" output.line endif note strlen 0> if note " <P>\n%s<BR>\n" output.line endif clear.fields ; \ ==================================================================== \ SECTION 5 \ ==================================================================== : <ADDRESS> cr ." The SGML-based Address Book" cr cr " address.htm" WRITE fopen fd.out ! setup.HTML clear.fields ; : </ADDRESS> " </BODY>\n" output.line " </HTML>\n" output.line fd.out @ fclose ; : <ENTRY> ; : </ENTRY> process.name process.phones process.the.rest ; \ ==================================================================== \ SECTION 6 \ ==================================================================== : <NAME> name read_into ; : </NAME> name -newline name msg cr ; : <BPHONE> bphone read_into ; : </BPHONE> bphone -newline ; : <APHONE> aphone read_into ; : </APHONE> aphone -newline ; : <HPHONE> hphone read_into ; : </HPHONE> hphone -newline ; : <FAX> fax read_into ; : </FAX> fax -newline ; : <TITLE> title read_into ; : </TITLE> title -newline ; : <CATEGORY> category read_into ; : </CATEGORY> category -newline ; : <COMPANY> company read_into ; : </COMPANY> company -newline ; : <ADDR1> addr1 read_into ; : </ADDR1> addr1 -newline ; : <ADDR2> addr2 read_into ; : </ADDR2> addr2 -newline ; : <CITY> city read_into ; : </CITY> city -newline ; : <ST> st read_into ; : </ST> st -newline ; : <ZIP> zip read_into ; : </ZIP> zip -newline ; : <NOTE> note read_into ; : </NOTE> note -newline ; \ ==================================================================== \ SECTION 7 \ ==================================================================== : & " &" ; : < " <" ; : > " >" ; " address.sgm" sengine_init sengine
The set of file words implemented in Until follows C conventions. In fact, most of the words use the same names as the equivalent C functions. If there are questions about calling sequences, refer to a C function reference manual. The code for the words documented here is found in FILE.C. The way each of these words operates is to collect the arguments from the stack, then call the corresponding C function. The Until word, errno returns the value of the C global variable errno. Any questions about word behavior can be answered by looking up the equivalent C function in the C compiler documentation. A file descriptor is the result of a call to fopen. The file descriptor is indicated by fd. The file related words are:
" test.fil" READ fopen descriptor ! ... descriptor @ fclose
descriptor @ fcr
descriptor @ feof if ." Got eof" else ." Not eof" then
descriptor @ ferror if ." File error" else ." No error" endif
descriptor @ fflush
descriptor @ fgetc
buffer size descriptor @ fgets 0= if ." EOF" thenNote that buffer is a counted string. The trailing <newline> is included in the buffer and a trailing NULL is included so buffer may be treated as either a NULL terminated or counted string. The string size is meaningless when the buffer is greater than 255 bytes.
" c:\until\outer.c" fnsplit .( Extension> ) dup strlen type cr .( File > ) dup strlen type cr .( Dir > ) dup strlen type cr .( Drive > ) dup strlen type cr
" name" READ fopen
The predefined modes are:
" A string: %s or a decimal number: %d"The arguments to fprintf are in the order used. For example:
" string" 65 " A string: %s or a decimal number: %d\n" fd @ fprintfwill write:
A string: string or a decimal number: 65with a trailing newline character to the file descriptor fd. The first argument goes with the first format specifier (" string" and the %s) and the second argument goes with the second format specifier (65 and %d) in this case.
Note that floating point values cannot be used with fprintf. Use fprintf_f to output floating point values.
" A floating point number: %e"The arguments to fprintf_f are in the order used. For example:
f# 123.456 " A floating point number: %e\n" fd @ fprintf_fwill write:
A floating point number: 123.4560with a trailing newline character to the file descriptor fd. The first argument goes with the first format specifier (123.456 and the %e).
Note that only floating point values can be used with fprintf_f. Use fprintf to output other values.
0 Beginning of the file (SEEK_SET) 1 From the current file position (SEEK_CUR) 2 End of the file (SEEK_END)The status is zero on success and non-zero on error. DOS only returns an error when attempting to position a file that has not been opened. Other operating systems may return additional errors.
" A string: %s or a decimal number: %d"
The arguments to printf are in the order used. For example:
" string" 65 " A string: %s or a decimal number: %d\n" printf
will write:
A string: string or a decimal number: 65
with a trailing newline character to STDOUT. The first argument goes with the first format specifier (" string" and the %s) and the second argument goes with the second format specifier (65 and %d) in this case.
Note that floating point values cannot be used with printf. Use printf_f to output floating point values.
" A floating point number: %e"The arguments to printf_f are in the order used. For example:
f# 123.456 " A floating point number: %e\n" printf_fwill write:
A floating point number: 123.4560with a trailing newline character to STDOUT. The first argument goes with the first format specifier (123.456 and the %e).
Note that only floating point values can be used with printf_f. Use printf to output other values.