-
Notifications
You must be signed in to change notification settings - Fork 0
Code Generator (AWK Language)
So far, the adventure has 10 objects. Each object consists of 5 attributes (the fields in the struct OBJECT). It is likely that a real-life adventure has hundreds, even thousands of objects, and the number of attributes per object increases. Keeping such a large list of objects and attributes in its current form would be difficult.
For example, when I added the wallField and wallCave objects previously, I had to do it in three different places: once in object.h (like #define) and twice in object.c (an element in the array objs, and a separate matrix for the tags). This is clumsy and error-prone.
Instead of keeping object.h and object.c by hand, I will start generating the files from a single source that best suits my needs. This new source file could be in any language I prefer (usually a domain-specific language), as long as I have the tools to convert it back to C. The following is a simple example. Consider the following layout to organize objects:
+----------------------------------------------------+
| |
| +--------------------------------------------+ |
| | | |
| | Raw C code (declarations) | |
| | | |
| +--------------------------------------------+ |
| +--------------------------------------------+ |
| | | |
| | | |
| | - ObjectName | |
| | AttributeName AttributeValue | |
| | AttributeName AttributeValue | |
| | ... | |
| | - ObjectName | |
| | AttributeName AttributeValue | |
| | AttributeName AttributeValue | |
| | ... | |
| | - ... | |
| | | |
| +--------------------------------------------+ |
| |
+----------------------------------------------------+
Based on the objects I've collected so far, I can build the following source file. The file name does not matter much; I simply called object.txt, to make it clear that it is related to object.h and object.c.
`+------------+`
`| object.txt |`
`+------------+`
`#include `
`#include "../include/object.h"`
`typedef struct object {`
`const char *description;`
`const char **tags;`
`struct object *location;`
`struct object *destination;`
`} OBJECT;`
`extern OBJECT objs[];`
`- field`
`description "an open field"`
`tags "field"`
`- cave`
`description "a little cave"`
`tags "cave"`
`- silver`
`description "a silver coin"`
`tags "silver", "coin", "silver coin"`
`location field`
`- gold`
`description "a gold coin"`
`tags "gold", "coin", "gold coin"`
`location cave`
`- guard`
`description "a burly guard"`
`tags "guard", "burly guard"`
`location field`
`- player`
`description "yourself"`
`tags "yourself"`
`location field`
`- intoCave`
`description "a cave entrance to the east"`
`tags "east", "entrance"`
`location field`
`destination cave`
`- exitCave`
`description "a way out to the west"`
`tags "west", "out"`
`location cave`
`destination field`
`- wallField`
`description "dense forest all around"`
`tags "west", "north", "south", "forest"`
`location field`
`- wallCave`
`description "solid rock all around"`
`tags "east", "north", "south", "rock"`
`location cave`
I made up the syntax myself, so it is safe to assume there are no standard tools to translate it to C. We will have to write our own code generator! Since this code generator will be a separate program, completely independent of our adventure program, we can write it in any language we like - not necessarily C. Here is one possible implementation, written in AWK:
`+------------+`
`| object.awk |`
`+------------+`
`BEGIN {`
`count = 0;`
`obj = "";`
`if (pass == "c2")`
`{`
`print "\nOBJECT objs[] = {";`
`}`
`}`
`/^- / {`
`outputRecord(",");`
`obj = ;`
`prop["description"] = "NULL";`
`prop["tags"] = "";`
`prop["location"] = "NULL";`
`prop["destination"] = "NULL";`
`}`
`obj && /^[ \t]+[a-z]/ {`
`name = ;`
` = "";`
`if (name in prop)`
`{`
`prop[name] = ;`
`}`
`else if (pass == "c2")`
`{`
`print "#error \"" FILENAME " line " NR ": unknown attribute '" name "'\"";`
`}`
`}`
`!obj && pass == (/^#include/ ? "c1" : "h") {`
`print;`
`}`
`END {`
`outputRecord("\n};");`
`if (pass == "h")`
`{`
`print "\n#define endOfObjs\t(objs + " count ")";`
`}`
`}`
`function outputRecord(separator)`
`{`
`if (obj)`
`{`
`if (pass == "h")`
`{`
`print "#define " obj "\t(objs + " count ")";`
`}`
`else if (pass == "c1")`
`{`
`print "static const char *tags" count "[] = {" prop["tags"] ", NULL};";`
`}`
`else if (pass == "c2")`
`{`
`print "\t{\t/* " count " = " obj " */";`
`print "\t\t" prop["description"] ",";`
`print "\t\ttags" count ",";`
`print "\t\t" prop["location"] ",";`
`print "\t\t" prop["destination"];`
`print "\t}" separator;`
`delete prop;`
`}`
`count++;`
`}`
`}`
We actually need to call this AWK script three times to generate the C sources:
**awk -v pass=h -f object.awk object.txt > ../include/object.h**
**awk -v pass=c1 -f object.awk object.txt > ../src/object.c**
**awk -v pass=c2 -f object.awk object.txt >> ../src/object.c**