Nick Grattan's Blog

About Microsoft SharePoint, .NET, Natural Language Processing and Machine Learning

An EDSL for Emitting HTML Tables in Web Parts using F#

with 5 comments

Embedded Domain specific languages (EDSL) are a great way of presenting functionality in a programming language for a solving a specific problem. Linq in VB.NET and C# is a great example. The F# language provides programming constructs for creating EDSLs – a good is example is this Logo implementation.  

When writing web parts in C# for SharePoint the System.Web “table” class or “table” HTML elements are most often used for controlling layout (“div” aficionados keep your ears covered…). The code quickly gets ugly and difficult to manage – there’s no direct control to ensure correct matching of opening and closing of tags. So how about writing an EDSL that provides a syntax and constructs for doing this?

Following on from a previous post on creating Web Parts in F# let’s look first at how the EDSL might be used. First, the mundane stuff for declaring and creating the System.Web controls – this will create a label and two input boxes: 

type public HelloWorldWP() =
    inherit WebPart()

    let labelRow1 = new Label()
    let inputRow1 = new TextBox()
    let inputRow2 = new TextBox()

    override x.CreateChildControls() =
        labelRow1.Text <- "Row 1 Label"
        x.Controls.Add(labelRow1)
        x.Controls.Add(inputRow1)
        x.Controls.Add(inputRow2)

The EDSL will now be used in the Render override to output the HTML to form the table. There will be two rows and two columns. Here’s the code:

   override x.Render (writer:HtmlTextWriter) =
        let myTable = [
                        Row
                        Cell
                        Content (Control labelRow1)
                        Cell
                        Content (Control inputRow1)
                        Row
                        Cell
                        Content (Static "Row 2 Static")
                        Cell
                        Content (Control inputRow2)
                        ]
        let tedsl = new TableEDSL()
        tedsl.WriteTable(myTable, writer)

The “myTable” F# list contains the “statements” that comprise the EDSL “program”. They are:

  • Row: Start a new table row.
  • Cell: Start a new table cell in the current table row.
  • Content: Output content to the current table cell. The content can be a System.Web control or static text.

Note that output of the “table” tag, and closing “tr” and “td” tags are implied in the language. Once the commands have been declared in the list “myTable”, an instance of the class “TableEDSL” is created and the method “WriteTable” executed to execute the statements in the list (“program”). This will result in the table being rendered in the web part:

Let’s now turn to how the language is created. Firstly, types are created to define what can be included in table (TableContent) and what can be included in a cell (CellContent) – these are the “statements” of the language used above:

type CellContent =
    | Static of string
    | Control of WebControl
    | InnerTable of TableContent list
and TableContent =
    | Row
    | Cell
    | Content of CellContent

These are the only two types required to define the language. Note:

  • These use F# discriminated union to declare what’s allowed in “CellContent” and “TableContent”.
  • These types are self-referencing hence the use of “and”. Notice how a “CellContent” can contain a TableContent, and this allows a table cell to embed other tables.
  • The “of” keyword declares the data type of the “statements” in the EDSL.

Now that the language is defined, a class need to be created that will interpret the language. First declare the class, some mutable values and two methods that check to see if “tr” and “td” tags need to be closed:

type TableEDSL() =

    let mutable haveCell = false
    let mutable haveRow = false

    member x.CheckCellClosure(writer:HtmlTextWriter) =
        if haveCell then
            writer.Write("</td>")
            haveCell <- false

    member x.CheckRowClosure(writer:HtmlTextWriter) =
        if haveRow then
            writer.Write("</tr>")
            haveRow <- false

The method “WriteTable” (which is called from the web part’s “Render” override) is passed the list of language “statements” and the HtmlTextWriter stream to which the HTML will be outputted:

    member x.WriteTable (theTable, writer:HtmlTextWriter) =
        writer.Write("<table>")
        List.iter (fun ele -> x.Table (ele, writer)) theTable
        x.CheckCellClosure(writer)
        x.CheckRowClosure(writer)
        writer.Write("</table>")

This method:

  • Outputs the “table” tag.
  • Iterates across the list of language statements, calling the method “Table” on each statement in the list.
  • Checks to see if there’s a cell to be closed through calling “CheckCellClosure”.
  • Checks to see if there’s a row to be closed through calling “CheckRowClosure”
  • Terminates the table.

Finally, we have the “Table” method that is called to execute each statement.

    member x.Table (theTable, writer:HtmlTextWriter) =
        match theTable with
            | Row ->
                haveRow <- true
                x.CheckCellClosure(writer)
                writer.Write("<tr>")
            | Cell ->
                x.CheckCellClosure(writer)
                haveCell <- true
                writer.Write("<td>")
            | Content s ->
                match s with
                    | Static str -> writer.Write(str)
                    | Control ctl -> ctl.RenderControl(writer)
                    | InnerTable tbl -> x.WriteTable(tbl, writer)               

This method uses the “F#” match construct to provide implementations for each of the allowable statements in the language. The “Content” statement has an additional “match” to determine what type output is required. In the case of “InnerTable”, the method “Table” is called recursively.

Here’s a “program” that uses an “InnerTable”:

        let myTable = [
                        Row
                        Cell
                        Content (Static "Embedded Table")
                        Cell
                        Content (InnerTable
                                    [
                                     Row
                                     Cell
                                     Content (Static "Inner Table 1")
                                     Row
                                     Cell
                                     Content (Static "Inner Table 2")
                                     ])
                        ]

The table will be displayed as:

The language can easily be extended to, for example, allow CSS styles and other table formatting instructions to be applied to the table.

Click Here for a page showing all the source code.

Advertisements

Written by Nick Grattan

May 12, 2010 at 12:50 pm

Posted in F#, Web Parts

5 Responses

Subscribe to comments with RSS.

  1. […] 12-May-2010: See this next post to find out how to write an EDSL for outputing Table […]

  2. […] Nick Grattan’s Creating Web Parts with F# and An EDSL for Emitting HTML Tables in Web Parts using F# […]

  3. How’s this better in F# , than some other language? Pyhton or JavaScript for example?
    F# version seems desperately complex, to me ?

    –DBJ

    DBJ

    June 22, 2010 at 12:20 am

    • Thanks for the comment. This is server side code, so to compare with JavaScript is not relevant. I haven’t used Python, so cannot compare with that language.
      F# is deeply integrated with .NET, and it gains much from that. It does take time to get used to the syntax.
      To be honest, I avoid language arguments! I leave this as a matter of choice for the developer.
      Regards, Nick.
      PS Apologies for delays in replying.

      Nick Grattan

      June 30, 2010 at 6:08 am

  4. […] May 12, 2010: An EDSL for Emitting HTML Tables in Web Parts using F# […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: