Diary.Dev

The unstructured ramblings of a developer gone wild.

  • Home
  • Archive
  • Contact
  • FeedSubscribe
  • Sign in
<< Converting a legacy application to Silverlight, Part 1 |

Building an expression parser

Posted on 27. January 2011 23:23 by Martin

The first part to building a general purpose expression parser is to understand what you will be parsing. That is, we need to define a schema for what an expression looks like. In my case, I knew that I wanted to use slightly XML documents that could be interpreted on the server.

I decided to include tokenized values in the format. This would let me define the XML document once and let it work for a wide variety of instances. That is, the notion of today is different depending on when it is being used. Instead of hard coding a date in the document, I use a token named “TODAY” that will replace with the current date at run time.

The next thing I needed to include was a way to identify fields that would be part of the LINQ Where clause. To make it flexible, I wanted to use the dotted notation for linked entities, such as “MyLinkField.ForeignField”. The XML Schema doesn’t really care about this. The parser class will be the one to understand and handle this.

I also needed the user to use hard coded values. that is, the expression field > 5 needs to know that “5” is a simple literal.

The last thing that I wanted to make sure was possible was nesting expressions inside one another. This would allow the possibility for complex expressions such as (ToDoByStaff.Login = TOKEN(User) AND (SubmitDate >= DATEADD(TODAY, DAYS, –5).

Without further ado, here is the schema that I came up with.

Expression.xsd
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <xs:schema id="Expression"
  3.     targetNamespace="http://tempuri.org/Expression.xsd"
  4.     elementFormDefault="qualified"
  5.     xmlns="http://tempuri.org/Expression.xsd"
  6.     xmlns:mstns="http://tempuri.org/Expression.xsd"
  7.     xmlns:xs="http://www.w3.org/2001/XMLSchema"
  8. >
  9.   <xs:complexType name="expression">
  10.     <xs:choice minOccurs="1" maxOccurs="unbounded">
  11.       <xs:element name="exp" type="expression" />
  12.       <xs:element name="lit">
  13.         <xs:complexType>
  14.           <xs:attribute name="value" type="xs:string" use="required" />
  15.         </xs:complexType>
  16.       </xs:element>
  17.       <xs:element name="token">
  18.         <xs:complexType>
  19.           <xs:attribute name="name" type="xs:string" use="required" />
  20.         </xs:complexType>
  21.       </xs:element>
  22.       <xs:element name="field">
  23.         <xs:complexType>
  24.           <xs:attribute name="name" type="xs:string" use="required" />
  25.         </xs:complexType>
  26.       </xs:element>
  27.     </xs:choice>
  28.     <xs:attribute name="func" type="xs:string">
  29.       <xs:annotation>
  30.         <xs:documentation>The name of the function for this expression.</xs:documentation>
  31.       </xs:annotation>
  32.     </xs:attribute>
  33.   </xs:complexType>
  34.  
  35.   <xs:element name="expression" type="expression" />
  36. </xs:schema>

And, here is a quick sample expression file that shows off every part of the schema.

Sample.xml
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <expression xmlns="http://tempuri.org/Expression.xsd">
  3.   <!-- (%USERID% = ToDoByStaff.login)  AND (SubmitDate >= (DATEADD(%TODAY%, "DAYS", -5))) -->
  4.   <exp func="AND">
  5.     <exp func="EQ">
  6.       <token name="USERID"/>
  7.       <field name="ToDoByStaff.Login"/>
  8.     </exp>
  9.     <exp func="GTE">
  10.       <field name="SubmitDate"/>
  11.       <exp func="DATEADD">
  12.         <token name="TODAY"/>
  13.         <lit value="DAYS"/>
  14.         <lit value="-5"/>
  15.       </exp>
  16.     </exp>
  17.   </exp>
  18. </expression>

 

Before I leave you for the day, here are some things to consider:

  • I don’t specify what functions, tokens, or fields are acceptable
  • The “root” expression needs to be a logical comparison function
  • The schema allows for an unlimited number of children to an expression

Why didn’t I specify what the valid functions are? I may want to add new ones to the expression parser. That’s also the reason for the other two choices.

And, why does the root expression need to be logical? This statement doesn’t make sense: SELECT * FROM TABLE1 WHERE 1+5. In my implementation, I don’t accept the “NOT” function. Instead, I construct the expressions around that limitation.

Finally, I didn’t put restrictions on the number of children to an expression so that I could handle any arbitrary function that is acceptable to the Entity Framework. I can also expose custom functions like DATEADD that need to be parsed into correct function calls that the framework accepts (in this example, it gets turned into AddDays because the second expression is the literal DAYS).

Next time, we’ll talk about the actual code that runs.

Tags:

Mission App | RIA Services

Submit to DotNetKicks...
Permalink | Comments (0)

Saving the comment

Add comment

Cancel reply to comment

Required Please choose another name


biuquote
  • Comment
  • Preview
Loading




Calendar

<<  January 2021  >>
MoTuWeThFrSaSu
28293031123
45678910
11121314151617
18192021222324
25262728293031
1234567

View posts in large calendar

Category list

  • RSS feed for HistoryHistory (1)
  • RSS feed for Mission AppMission App (2)
  • RSS feed for Non CodeNon Code (1)
  • RSS feed for RIA ServicesRIA Services (2)
  • RSS feed for SilverlightSilverlight (1)
 

Copyright © 2011.

Design by Free Flash Templates | Adapted by ExtraGeek