If they work like the C pre-processor directives then processing will consist of passing HTML to the output and handling any $xxx keywords (directives?).
->Yes, actually this skin file, which I do not have the sources, does work exactly like a C pre-processor. Can you please in more detail tell me how to code this, or if there are any programs out there that work similarily to this (including the source code) can you recommend me any?
Hello, I'm a C++ amatuer trying to develop a message board on CGI. I have downloaded various message boards and experimented with many of them. From all that I've seen, I was most interested with this one message board where you can change skins.
This message board allowed users to develop their own skin files in html. In that skin file, there were commands like ($if variable=true_or_false)which was a simple form of the if statement from the C++. How on Earth did he do this?
Well as I do not even know what the product is (and no, please do not tell me as I am not that interested!), and have not seen the source code I cannot give you an in depth analysis. If you really want to know how it is done in exact detail then I suggest you track down the source code to this product if it is available.
However in general you do what all programs do: I P O - Input Process Output.
So the application reads the skin file and makes sense of it some how. Now as you indicate that it is HTML maybe the file is processed, the added skin-variables and operators evaluated and the results output to a pure HTML file - or equivalent - to be used with an HTML processor (e.g. the MS Windows browser control) to interpret and render the skin. Maybe not. Personally I would not use HTML unless I had a good browser like entity - component, library, control, widget, etc.. - that I could use in my application to do the hard work of rendering the HTML.
I would guess that the $if you saw in this skin file is in fact less like the C/C++ if statement and more like the C pre-processor's #if directive - which allows for conditional compilation of sections of code:
#if defined DEBUG
// code only included for compilation in debug builds
DEBUG would usually be defined using a compiler command line switch -D or /D being popular choices and work as if:
had been seen by the pre-processor. Note that on some systems - notably UN*X like systems - the C pre-processor is a separate program (on UN*X like systems it is called, confusingly for C++ programmers, cpp).
However you do not say what the usage of the $if boolean_value is so of course I could be wrong.
In any case the process would be similar - you read the file piece by piece and build up a picture of the parts you are interested in and do some default operation with the rest. For example maybe this file also contains:
$var name = value
definitions as well. Well the program may read the extended HTML skin file and, unless in a special state, pass all the input to the output. On reading one of the special symbols: $var, $if etc.. special actions are performs. For example the $var definition handling would read the name and the value (if any) and add them to a collection of such variables. Keeping it simple, if all the values were strings then you might use a string, string mapping (for example std::map<std::string, std::string> ) to associate the variable name with its value. The $if processing would read the condition expression after the $if and determine whether the result were true or false and react accordingly. Taking your example, the variable name is read and used as the key to the variable map. Assuming it is found then its value part is used as the value for that part of the expression; the equals is read and the right hand side evaluated - which is either true or false - these are special names that the code recognises and uses as literal values. The result of the expression is true if the value of the variable on the left hand side matches that of the right hand side. Otherwise it is false.
Assuming the latter as an example. If the $if expression resolves to true then input will (still) be processed - HTML is passed directly to the output, other $xxx keywords are acted upon, and a note is made that there is an active $if block - this may require the use of a stack to push the previous processing state so it may be resumed (by popping it off the stack) when the $endif (as an example syntax) is encountered. If the $if expression resolves to false then processing of input is skipped over (i.e. read and not acted on or passed to the output) until the $endif is encountered.
The process I have described is a very simple one - all processing is done immediately. Larger languages do not work like that - they may make more than one pass over the code (the skin file in this example), each one adding to the information previously obtained by earlier passes. If you wish to know more then I suggest you read up on tokenisation, parsing, expression evaluation and other programming language topics such as interpreters, compilers and pre-processors. As I intimated earlier, how do you think your C++ compiler makes sense of C++ source code?
Well I am not going to tell you how to code it in great detail as if I did I would effectively be writing the code for you, and I also do not have the complete specification of what you wish to implement. Also I think that doing so is too great a task for a volunteer reply to questions posted on the likes of AllExperts.
It may surprise you that just because I am an expert does not mean I have the answers to all development tasks in my head – I have to work the details as well.
You can probably find examples similar to your requirements around in source code form – try a search on Google (http://www.google.com/
) or similar search engine site. Another site you might like to search is the SourceForge (http://sourceforge.net
) open source project hosting site – it may even have a project or two very similar to yours. If you wish to look at the source of a C pre processor then you might like to try the GNU C Preprocessor (http://gcc.gnu.org/onlinedocs/cpp/
). Note that you may find examples in C rather than C++.
When I replied to your original question I assumed you were familiar with certain fairly basic concepts – such as handling files – open, close, read, write etc. and some familiarity with the standard C++ library and concepts such as strings and maps (a.k.a. dictionaries).
Your basic model is that most data is passed from the input file to the output file. Now you have to do some design to define the pre-processor directives. If you wish to have similar directives to those used in C then you will have the likes of include, define, if, else, else if, endif. As in C and your original question you will probably wish to add some sort of special sequence of characters (1 or more) to these directive names, such as $ or #. You might also like to restrict them to being the first non-whitespace character on a line – as in C. Both of these rules will make life easier for you as the developer of the parser. You might require additional restrictions as line breaks are not that important in HTML for example:
I think is a legal HTML fragment but would you wish to allow directives within a tag definition? It may be useful. It may be too much trouble. Your call.
You will also have to decide how to handle the likes of text blocks that contain text that just happen to look like your directives:
This is a special directive
You will have to cope with such situations. For example you may consider further restricting where you can put your directives to exclude text blocks – which means that you will have to have more understanding of the structure of the HTML you are passing over, and would probably be very difficult to accomplish.
A simpler approach would be to use the escape character ploy. If such a situation occurs as above then you escape the $ to indicate that it is not a directive but should be passed directly to the output, less the escape character. In this case your source document contains something like:
This is a special directive
and produces the original version when processed. The beauty of using another $ to indicate an escaped character is that you can add it to the list of directives, and so simplify the parsing.
So now you have the basic layout. The pseudo code below is off the top of my head so probably needs tweaking to get absolutely correct, - e.g. I get the character and process it without checking for end of file (eof) again - but it gives you the idea. White space is a space or tab character. Also I hope the indentation formatting has got to you OK. Try using a fixed spaced font if it is not clear.
Open the input and output files
Set start of line flag
Define white space buffer
While input file not at eof:
If at start of line and $ read:
process as directive
If at start of line and white space read:
append character to white space buffer
If new line read:
write buffered white space, clear buffer
set start of line flag
write character to output file
If other character:
If at start of line:
write buffered white space, clear buffer
clear start of line flag
write character to output file
Of course this is only one way to do it – particularly in the way I buffer white space characters until another character is reached – this is to preserve all non-directive characters in the file. Note that when processing as a directive you have to ensure you leave the state ready for the next part of the input – generally at the start of the next line with a cleared white space buffer. The white space buffer could be just a std::string.
You can of course code the above using constructs other than if – a switch statement come immediately to mind.
Now to the question of how you actually perform the sort of results of a directive. First let's characterise them:
Include : switches input file
If, else, elif: effects processing of input
Define, Undefine: creates or removes a name, value pair
Include remembers the current open file and opens the included file then at the end of the included file returns to processing the previous file. To handle this situation you could use a stack of file objects. Each include causes the current file object to be pushed onto the stack and each end of file causes a file to be popped off the stack and becomes the currently processed file again. If the stack is empty and an end of file is reached then that was the original input file and the program run is finished. Note here that you push references (in fact most likely pointers) to the open file objects (e.g. std::ifstream) not the name of the file.
If will either continue processing as normal or will suspend processing to some extent - nothing is written out and most directives are ignored. However endif else and elif have to be processed even if the if block is not included as they end the block. As these conditional blocks can be nested you have to keep track of which ones are active – a job for another stack. This time the stack would contain objects describing the state of each block – at least an indicator of which block type as if and elif can be followed by elif and else and else cannot. Else and elif are handled in a similar way.
Now if and elif require processing the following expression which may contain names of defined values, literal values and operators. Which operators you support is up to you and I suggest that you look up a good text on expression evaluation as it is a subject that has many variations and is too long to go into here. I would do the same if I were implementing an expression evaluator as it is not something I do every day.
Define and undefine maintain a mapping of names of the defined items and the values of these items. As I suggested previously you can use a map type such as a std::map<std::string, std::string> to keep these name,value pairs. Whether you allow just simple strings as defined values or some other more complex type such as macros that may require parameter substitution is up to you. Personally I would start with the simple option of just using a string as the value – converting to a numeric or Boolean value as and when required.
Now I have not permitted in this scheme for defined names to be used anywhere other than in if or elif expressions – which means that much of the processing is cantered aaround lines. You might like to consider how you would go about extending the scheme to allow defined names to be used as substituted values within the HTML text proper, as you would use a #define macro in C or C++ code. Maybe you use a syntax such as $<defined_name>$ such as:
$if type==1 or type==3
$define SPEED slow
$define SPEED normally
You are running $SPEED$
In the above example $SPEED$ would be replaced with either slow or normally. Again you can use escaping to permit the above to written verbatim:
You are running $$SPEED$$
But note that the processing of the $$ now needs to not be line based, at least not in all situations.