Copyright ©1994 Don Yacktman. Version 1.0.
Notes on the MiscParseTable routines |
Module Description |
The MiscParseTable functions are four functions which are useful for dealing with the ASCII table
files used extensively throughout NEXTSTEP. Examples of these files are the table files inside the
WorkSpace .addresses file packages, the data.classes file inside .nib file packages, and the
PB.project file. The MiscParseTable functions provide a simple mechanism for reading and
writing these files by translating between the ASCII file and a very specific structure (described
below) built up of MiscString, MiscList, and MiscDictionary objects.
To read a table file, use MiscParseTableFile() or MiscParseTableStream(), depending upon whether you have the data available in a UNIX file or in a NXStream. To write a file from the appropriate data structure, use either MiscWriteTableFile() or MiscWriteTableStream(). The ASCII files used by NeXT can contain three types of items. First are strings. These are usually enclosed by quotes (""") unless they are a single word and/or number. There are also lists of items which are enclosed by parenthesis, with commas separating each item. Finally are tables filled with key/value pairs. These are enclosed by braces ("{}") and on each successive line use a string for a key, followed by an equal sign ("="), followed by any of the three item types listed here as a value, and ending with a semicolon (";"). The mapping used by the MiscParseTable functions is simple; a string becomes a MiscString, a list becomes a MiscList, and a table become a MiscDictionary. (Note that the MiscDictionary object is simply a HashTable that maps string keys to object (id) values.) Thus, the tables and lists can be made up of strings, tables, or lists. Included below is an attempt at the BNF grammar used by this file format. By way of example, here is a sample file and a diagram showing what it maps to when parsed: (This file happens to be part of the PB.project file for the TreeView MiscKit example.) |
INSTALLDIR = /LocalApps;
APPICON = Tree.tiff;
GENERATEMAIN = YES;
DOCICONFILES = (Tree2.tiff);
FILESTABLE = {
OTHER_LIBS = (Media_s, MiscKit, NeXT_s);
OTHER_SOURCES = (Makefile);
OTHER_LINKED = ();
};
LOCALIZABLE_FILES = {
InfoPanel.nib;
TreeView.nib;
DocWindow.nib;
};
PROJECTNAME = TreeView;
![]() Fig 1. Example file's parsed data structure.
More complex files are possible, however, since you may nest objects in lists and tables--making it possible to build lists of tables and tables within tables, for example. |
Exported Functions |
MiscParseTableFile![]() Opens a stream on fileName and calls MiscParseTableStream to parse the file. Returns the id of the root MiscDictionary making up the file. Errors are printed to the console, but are not currently reported to the calling routine. The ASCII read from the file should conform to the BNF grammar given below.
MiscParseTableStream Uses a recursive descent parser to parse an ASCII table file from off the stream aStream. A MiscDictionary object, the root object in the file, is returned. Errors are printed to the console, but are not currently reported to the calling routine. The ASCII read from the stream should conform to the BNF grammar given below.
MiscWriteTableFile Opens a stream on fileName and calls MiscWriteTableStream to create an ASCII table file. The root of the file, a MiscDictionary, should be provided in tableRoot. Returns zero. (In the future, an error code may be returned.) The ASCII placed in the file will conform to the BNF grammar given below.
MiscWriteTableStream Uses a recursive routine to write an ASCII table file to the stream aStream. The root of the file, a MiscDictionary, should be provided in tableRoot. Returns zero. (In the future, an error code may be returned.) The ASCII placed on the stream will conform to the BNF grammar given below. |
Exported Methods |
These routines add categories to MiscDictionary, MiscList, and MiscString which are used to
parse and write the table files. Each object is responsible to write itself as an ASCII
representation or read itself from an ASCII representation. The following methods are the ones
used for this purpose. They should be considered private, but may be used by your programs for
other purposes and may be overridden to change functionality. Be careful subclassing these
methods since some use other private methods and functions in their implementations.
- parseFromASCIIStream:(NXStream *)aStream Parses an object from an ASCII stream, passed in as aStream. Returns self. The table and list versions assume that the leading "{" or "(" have already been read from the stream and leave the trailing "}" or ")" on the stream, but the string will expect the leading """ to still be on the stream and will remove the trailing """. - writeASCIIToStream:(NXStream *)aStream Writes an object to an ASCII stream, passed in as aStream. Returns self. The ASCII written will include the appropriate delimiter characters ({}, (), and "") around the object. Since tables need to be written without the brackets in some cases, there is a -writeInnerASCIIToStream: method for the MiscDictionary object which will write the object without the "{}" characters. |
Known Bug |
Some of the NeXT files have string arrays in them stored as a single string constant with \000 (NULL) as a separator withing the string constant. The current implementation of the MiscTableParse routines strips the NULLs out of the string. This means that if you read a file in and then write it back out, there are possible problems. The "Address Book Shelf" key in address books is a good example (and only known case) of this problem. The most likely fix is to have a future implementation detect these NULLs and return a MiscStringArray instead of a MiscString. Since the fix is rather involved, it could not be made ready in time for the current release. |
BNF grammar for table files |
Here is the BNF implemented by this code, which is more or less accurate. Start parsing with the
File non-terminal. ":=" to define non-terminals and nil is used to denote when a non-terminal can
reduce to nothing (ie, it is not required).
Literals (terminals) are given in unitalicized bold and use the following: Starting non-terminal is File: File: Dictionary: |
Dictionary Line | (Dictionaries | |
Line |
Line: |
String = Association ; | (string is hash key, uniqued by parser) | |
String ; | (string and association are identical) | |
nil |
Association: |
{ Dictionary } | (Keyed groups of objects) | |
( List ) | (Groups of unkeyed objects) | |
String |
List: |
List , Association | (Lists are comma separated groups) | |
Association | ||
nil |
String: | (some don't have to be quoted) | |
![]() | (quoted string) | |
![]() | (non-quoted string) | |
Literal: | (non-quoted string) | |
![]() | ||
![]() | ||
Eliteral: | (quoted string) | |
![]() | ||
![]() |
ELchar:![]() |
\\ | (backslash character) | |
\" | (quote character) | |
\t | (tab) | |
\n | (newline) | |
\r | (return) | |
\0x ([0-9A-Fa-f])* | (hex constant) | |
\0 [0-7]* | (octal constant) | |
\ [1-9][0-9]* | (decimal constant) |