| 1 | \section{wxExpr overview}\label{exproverview} |
| 2 | |
| 3 | wxExpr is a C++ class reading and writing a subset of Prolog-like syntax, |
| 4 | supporting objects attribute/value pairs. |
| 5 | |
| 6 | wxExpr can be used to develop programs with readable and |
| 7 | robust data files. Within wxWidgets itself, it is used to parse |
| 8 | the {\tt .wxr} dialog resource files. |
| 9 | |
| 10 | {\bf History of wxExpr} |
| 11 | |
| 12 | During the development of the tool Hardy within the AIAI, a need arose |
| 13 | for a data file format for C++ that was easy for both humans and |
| 14 | programs to read, was robust in the face of fast-moving software |
| 15 | development, and that provided some compatibility with AI languages |
| 16 | such as Prolog and LISP. |
| 17 | |
| 18 | The result was the wxExpr library (formerly called PrologIO), which is able to read and write a |
| 19 | Prolog-like attribute-value syntax, and is additionally capable of |
| 20 | writing LISP syntax for no extra programming effort. The advantages of |
| 21 | such a library are as follows: |
| 22 | |
| 23 | \begin{enumerate}\itemsep=0pt |
| 24 | \item The data files are readable by humans; |
| 25 | \item I/O routines are easier to write and debug compared with using binary files; |
| 26 | \item the files are robust: unrecognised data will just be ignored by the application |
| 27 | \item Inbuilt hashing gives a random access capability, useful for when linking |
| 28 | up C++ objects as data is read in; |
| 29 | \item Prolog and LISP programs can load the files using a single command. |
| 30 | \end{enumerate} |
| 31 | |
| 32 | The library was extended to use the ability to read and write |
| 33 | Prolog-like structures for remote procedure call (RPC) communication. |
| 34 | The next two sections outline the two main ways the library can be used. |
| 35 | |
| 36 | \subsection{wxExpr for data file manipulation}\itemsep=0pt |
| 37 | |
| 38 | The fact that the output is in Prolog syntax is irrelevant for most |
| 39 | programmers, who just need a reasonable I/O facility. Typical output |
| 40 | looks like this: |
| 41 | |
| 42 | \begin{verbatim} |
| 43 | diagram_definition(type = "Spirit Belief Network"). |
| 44 | |
| 45 | node_definition(type = "Model", |
| 46 | image_type = "Diamond", |
| 47 | attribute_for_label = "name", |
| 48 | attribute_for_status_line = "label", |
| 49 | colour = "CYAN", |
| 50 | default_width = 120, |
| 51 | default_height = 80, |
| 52 | text_size = 10, |
| 53 | can_resize = 1, |
| 54 | has_hypertext_item = 1, |
| 55 | attributes = ["name", "combining_function", "level_of_belief"]). |
| 56 | |
| 57 | arc_definition(type = "Potentially Confirming", |
| 58 | image_type = "Spline", |
| 59 | arrow_type = "End", |
| 60 | line_style = "Solid", |
| 61 | width = 1, |
| 62 | segmentable = 0, |
| 63 | attribute_for_label = "label", |
| 64 | attribute_for_status_line = "label", |
| 65 | colour = "BLACK", |
| 66 | text_size = 10, |
| 67 | has_hypertext_item = 1, |
| 68 | can_connect_to = ["Evidence", "Cluster", "Model", "Evidence", "Evidence", "Cluster"], |
| 69 | can_connect_from = ["Data", "Evidence", "Cluster", "Evidence", "Data", "Cluster"]). |
| 70 | \end{verbatim} |
| 71 | |
| 72 | This is substantially easier to read and debug than a series of numbers and |
| 73 | strings. |
| 74 | |
| 75 | Note the object-oriented style: a file comprises a series of {\it clauses}. |
| 76 | Each clause is an object with a {\it functor}\/ or object name, followed |
| 77 | by a list of attribute-value pairs enclosed in parentheses, and finished |
| 78 | with a full stop. Each attribute value may be a string, a word (no quotes), |
| 79 | an integer, a real number, or a list with potentially recursive elements. |
| 80 | |
| 81 | The way that the facility is used by an application to read in a file is |
| 82 | as follows: |
| 83 | |
| 84 | \begin{enumerate}\itemsep=0pt |
| 85 | \item The application creates a wxExprDatabase instance. |
| 86 | \item The application tells the database to read in the entire file. |
| 87 | \item The application searches the database for objects it requires, |
| 88 | decomposing the objects using the wxExpr API. The database may be hashed, |
| 89 | allowing rapid linking-up of application data. |
| 90 | \item The application deletes or clears the wxExprDatabase. |
| 91 | \end{enumerate} |
| 92 | |
| 93 | Writing a file is just as easy: |
| 94 | |
| 95 | \begin{enumerate}\itemsep=0pt |
| 96 | \item The application creates a wxExprDatabase instance. |
| 97 | \item The application adds objects to the database using the API. |
| 98 | \item The application tells the database to write out the entire database, |
| 99 | in Prolog or LISP notation. |
| 100 | \item The application deletes or clears the wxExprDatabase. |
| 101 | \end{enumerate} |
| 102 | |
| 103 | To use the library, include "wxexpr.h". |
| 104 | |
| 105 | \subsection{wxExpr compilation} |
| 106 | |
| 107 | For UNIX compilation, ensure that YACC and LEX or FLEX are on your system. Check that |
| 108 | the makefile uses the correct programs: a common error is to compile |
| 109 | y\_tab.c with a C++ compiler. Edit the CCLEX variable in make.env |
| 110 | to specify a C compiler. Also, do not attempt to compile lex\_yy.c |
| 111 | since it is included by y\_tab.c. |
| 112 | |
| 113 | For DOS compilation, the simplest thing is to copy dosyacc.c to y\_tab.c, and doslex.c to |
| 114 | lex\_yy.c. It is y\_tab.c that must be compiled (lex\_yy.c is included by |
| 115 | y\_tab.c) so if adding source files to a project file, ONLY add y\_tab.c |
| 116 | plus the .cc files. If you wish to alter the parser, you will need YACC |
| 117 | and FLEX on DOS. |
| 118 | |
| 119 | The DOS tools are available at the AIAI ftp site, in the tools directory. Note that |
| 120 | for FLEX installation, you need to copy flex.skl into the directory |
| 121 | c:/lib. |
| 122 | |
| 123 | If you are using Borland C++ and wish to regenerate lex\_yy.c and y\_tab.c |
| 124 | you need to generate lex\_yy.c with FLEX and then comment out the `malloc' and `free' |
| 125 | prototypes in lex\_yy.c. It will compile with lots of warnings. If you |
| 126 | get an undefined \_PROIO\_YYWRAP symbol when you link, you need to remove |
| 127 | USE\_DEFINE from the makefile and recompile. This is because the parser.y |
| 128 | file has a choice of defining this symbol as a function or as a define, |
| 129 | depending on what the version of FLEX expects. See the bottom of |
| 130 | parser.y, and if necessary edit it to make it compile in the opposite |
| 131 | way to the current compilation. |
| 132 | |
| 133 | %To test out wxExpr compile the test program (samples/wxexpr/wxexpr.exe), |
| 134 | %and try loading test.exp into the test |
| 135 | %program. Then save it to another file. If the second is identical to the |
| 136 | %first, wxExpr is in a working state. |
| 137 | |
| 138 | \subsection{Bugs} |
| 139 | |
| 140 | These are the known bugs: |
| 141 | |
| 142 | \begin{enumerate}\itemsep=0pt |
| 143 | \item Functors are permissible only in the main clause (object). |
| 144 | Therefore nesting of structures must be done using lists, not predicates |
| 145 | as in Prolog. |
| 146 | \item There is a limit to the size of strings read in (about 5000 bytes). |
| 147 | \end{enumerate} |
| 148 | |
| 149 | \subsection{Using wxExpr} |
| 150 | |
| 151 | This section is a brief introduction to using the wxExpr package. |
| 152 | |
| 153 | First, some terminology. A {\it wxExprDatabase}\/ is a list of {\it clauses}, |
| 154 | each of which represents an object or record which needs to be saved to a file. |
| 155 | A clause has a {\it functor}\/ (name), and a list of attributes, each of which |
| 156 | has a value. Attributes may take the following types of value: string, word, |
| 157 | integer, floating point number, and list. A list can itself contain any |
| 158 | type, allowing for nested data structures. |
| 159 | |
| 160 | Consider the following code. |
| 161 | |
| 162 | \begin{verbatim} |
| 163 | wxExprDatabase db; |
| 164 | |
| 165 | wxExpr *my_clause = new wxExpr("object"); |
| 166 | my_clause->AddAttributeValue("id", (long)1); |
| 167 | my_clause->AddAttributeValueString("name", "Julian Smart"); |
| 168 | db.Append(my_clause); |
| 169 | |
| 170 | ofstream file("my_file"); |
| 171 | db.Write(file); |
| 172 | \end{verbatim} |
| 173 | |
| 174 | This creates a database, constructs a clause, adds it to the database, |
| 175 | and writes the whole database to a file. The file it produces looks like |
| 176 | this: |
| 177 | |
| 178 | \begin{verbatim} |
| 179 | object(id = 1, |
| 180 | name = "Julian Smart"). |
| 181 | \end{verbatim} |
| 182 | |
| 183 | To read the database back in, the following will work: |
| 184 | |
| 185 | \begin{verbatim} |
| 186 | wxExprDatabase db; |
| 187 | db.Read("my_file"); |
| 188 | |
| 189 | db.BeginFind(); |
| 190 | |
| 191 | wxExpr *my_clause = db.FindClauseByFunctor("object"); |
| 192 | int id = 0; |
| 193 | wxString name = "None found"; |
| 194 | |
| 195 | my_clause->GetAttributeValue("id", id); |
| 196 | my_clause->GetAttributeValue("name", name); |
| 197 | |
| 198 | cout << "Id is " << id << ", name is " << name << "\n"; |
| 199 | \end{verbatim} |
| 200 | |
| 201 | Note the setting of defaults before attempting to retrieve attribute values, |
| 202 | since they may not be found. |
| 203 | |