Hi fellow Monks,

I'd like this post to be perceived as a follow up on my current efforts at designing/implementing a CFML-like parser for Perl.

CFML is a ColdFusion scripting language that is widely used for building decent websites fast and easy. To read more on CFML read here. In my earlier post -- which also touches on slightly outdated design --, I also discuss various advantages of having a CFML-parser module in Perl (otherwise, besides awesome opportunity to learn more of Perl, I wouldn't have started on this project).

At the moment, this module is in it's early beta stage. I've implemented major pieces of the code that take care of parsing and building appropriate (ready to 'compile') tag parse trees. For example, it is now possible to parse CFML templates like this:
---------------------
<cfset foo_name1 = 'vlad'> <cfset res = 2*2> <cfset var1 = 'foobar'> <cfif> <cfset var1 = 'val1'> <cfoutput> var1 = #var1# </cfoutput> </cfif> Hello World! Welcome, <cfoutput query='bar'>#foo_name1#</cfoutput>! <cfoutput>Math result = #res#</cfoutput>
---------------------
To see this module in action, visit my test page specially crafted for that purpose. Note that 'Template Variables' form field would only affect 'global' variables and therefore if you were to type 'var1=foobar' and executed the template included above, the resultant output wouldn't be changed (that is, you'll still see something like 'var1 = val1', instead of 'var1 = foobar'). However, this is nearly to be changed as I'd like to conform to strict CFML specifications by making all variables 'global'. In which case, second <cfset> tag would reset var1 variable from 'foobar' (value that it was initialized to earlier) to 'val1'.

I'd encourage you to play around with the test page and see if you find anything interesting etc. Any comments would be greatly appreciated! ;-). I should also apologize for subjecting you to a rather cluttered code (ton's of semi-useful comments etc.) and promise that all of it will be cleaned up prior to it's final release.

For those who may find this subject interesting, I've included latest source code for download on my projects page.

Here's a list of a few points that concern me with regards to overall design/strategy etc.:
  1. Advantages/Pitfalls of current Parsing algorithm and ways it could be improved. - (refer to CFML::Parser::_parse() routine for more info).
  2. How should 'parse tree' produced by the _parse() routine execute?
  3. Mechanism of enforcing variable scoping etc. (Could be dropped for CFML templates, but might be useful for any other 'tag' based scripting language).
  4. OO related design. Will it be easy to build/extend upon? For example, each tag encountered in a template is parsed into a corresponding 'handler' object which implements the tag's functionality. Also, some tags may be nested and contain other tags/variables. Inside CFML::Parser package, tags that may contain other tags are derived from special TAG::Container class.
  5. Is there a better alternative to OO approach? What I was thinking about was associating each CFML tag with a subroutine inside CFML::Parser, that would be tasked to handle tag's functionality. This way, <cfloop> (loop tag) could be implemented with a recursive subroutine handler and etc. However, I seem to lean more towards OO approach for reasons such as ease of maintaining/extending code etc. However, again, I'm not entirely sure (and even current implementation, which relies heavily on OO approach, could be scraped for a better alternative), besides one fact that implementing classes to handle certain tags is similar to how Allaire implemented their parser. Therefore, in order to be able to reproduce most of CFML functionality, it might prove useful to stick to OO approach. Also, CFXAPI might be better understood if implemented via abstract objects etc.
  6. Ideally, this module should be implemented such that new functionality could be easily added (such as new tags etc.) via certain API. What I'm thinking about is providing a mechanism similar to Allaire's Java CFXAPI (API for extended tags). According to Allaire's implementation, they have a CustomTag abstract class which is then used to construct derived classes to implement 'custom' tag functionality.


In the meantime, I'll try to put up some useful design/spec docs for you to refer to. ;-).



"There is no system but GNU, and Linux is one of its kernels." -- Confession of Faith

Replies are listed 'Best First'.
Re: CFML Parser - testing. Design/Implementation suggestions.
by jlongino (Parson) on Jan 02, 2002 at 00:36 UTC
    I was playing with your test page and was confused at first about what the Template Variables field was for. Then I read your post more carefully and found the bit about 'global' variables.

    After seeing a couple of examples of templates elsewhere, I modified your default template to look so:

    Hello World! Welcome, <cfoutput query='bar'>#foo_name#</cfoutput>! <cfoutput>Math result = #res#</cfoutput>
    For those who haven't seen the default template, I removed the first two lines that initialize the template variables foo_name and res. I then used the following in the Template Variables field: foo_name="Jim",res=5**3 I noticed that the res value was not calculated in the Parser Output window:
    Hello World! Welcome, "Jim"! Math result = 5**3
    What function or syntax would be necessary necessary in the template to cause it to interpolate the expression? Apologies if this is a silly question, but I don't know anything about Cold Fusion though I found your post interesting and as a nutty but wise monk once chided me:
    "testers" can't hurt ;)(and usually, they learn something)
    Touché.

    --Jim

      Thanks for your question ;-). There's nothing wrong with your quest, and I deeply appreciate your curiosity. ;D.

      The probelm with my current template parser is that the I use standard Perl eval() for expressions at the moment and _only_ if you set a variable via <CFSET> tag. Setting a variable via parameter list (the test page) is equivalent to this assignment in perl:
      $foo_var = 'foobar';

      If done via CFSET however, the expression should be evaluated, since that's how CFSET handler class was coded to perform.
      Whilst Perl eval()'s enough for testing, there'll have to be a CFML compatible eval() in place in order for this to work well. In many respects Perl eval() would be similar to CFML eval.

      "There is no system but GNU, and Linux is one of its kernels." -- Confession of Faith