JavaScript expression parser in Silverlight
Hey everybody! I have a Silverlight project that needs to help users create JavaScript logical expressions (e.g. "var1 != var2 && var3 >= var4"). Very simple. The problem is that I do not have much time to create one myself because tweaking the transition table would be a little time consuming.
So I was searching online for one, but all I found was a couple classes that help you validate the JavaScript, but I need a way to load it, parse it and then write it back. So I was wondering if anyone has any ideas about how I can do it. I am assuming it will not require much code, and it does not have to be a C# example either. If only you could give me some ideas in C++ or even pseudo code, that would help a lot.
Thank you,
V开发者_运维百科ladimir
Create a recursive descent parser by hand. It's fun!
First define a grammar:
Expression -> SimpleExpression ExpressionTail
(Expression)
SimpleExpression -> Atom Inequality Atom
ExpressionTail -> && Expression |
|| Expression |
e
Atom -> Variable | Literal
Inequality -> == | != | > | >= | < | <=
(This grammar doesn't handle operator precedence, but it is easy to write a grammar that will.)
Next, create a lexical analyzer that will read the input string and create tokens so the string "var1 != 4.0" would be translated to a list of {Variable, !=, Literal}. This can be hacked together, or if you want to get fancy you can use a finite state machine to analyze the string.
Now create a recursive descent parser to parse the list of tokens into an expression tree.
public class Parser {
public Parser() {
}
public Expression parse( LexicalAnalyzer lex) {
lex.advance();
return expression( lex);
}
private Expression expression( LexicalAnalyzer lex) {
switch( lex.current().type() ) {
case Token.OpenParen:
lex.advance();
Expression expression = expression( lex);
lex.eat( Token.CloseParen);
return expression;
case Token.Literal:
case Token.Variable:
return expressionTail( lex, simpleExpression(lex));
default:
throw new Exception( "ParseError: Expecting (, variable, or literal at character " +
lex.character());
}
}
private SimpleExpression simpleExpression( LexicalAnalyzer lex) {
Atom atom = atom( lex);
switch( lex.current().type() ) {
case Token.Equal:
lex.advance();
return new Equal( atom, atom(lex));
case Token.NotEqual:
lex.advance();
return new NotEqual( atom, atom(lex));
...
default:
throw new Exception( "ParseError: Expecting ==, !=, <, <=, >, >= at" +
lex.character());
}
}
private Expression expressionTail( LexicalAnalyzer lex, SimpleExpression expression) {
switch( lex.current().type() ) {
case Token.LogicalOr:
lex.advance();
return new OrExpression( expression, expression(lex) );
case Token.LogicalAnd:
lex.advance();
return new AndExpression( expression, exptression(lex) );
default:
return expression;
}
}
private Atom atom( LexicalAnalyzer lex) {
switch( lex.current().type() ) {
case Token.Literal:
Literal literal = new Literal( lex.current() );
lex.advance();
return literal;
case Token.Variable:
Variable var = new Variable( lex.current() );
lex.advance();
return var;
default:
throw new Exception("ParseError: ...");
}
}
}
public interface Expression {
boolean evaluate( VariableValues values);
}
public interface SimpleExpression implements Expression {
}
public class Equal implements SimpleExpression {
private final Atom left, right;
public Equal( Atom left, Atom right) {
this.left = left;
this.right = right;
}
public boolean evaluate( VariableValues values) {
return left.evaluate(values) == right.evaluate(values);
}
}
public class NotEqual implements SimpleExpression {
private final Atom left, right;
public NotEqual( Atom left, Atom right) {
this.left = left;
this.right = right;
}
public boolean evaluate( VariableValues values) {
return left.evaluate(values) != right.evaluate(values);
}
}
public class OrExpression implements Expression {
private final Expression left, right;
public OrExpression( Expression left, Expression right) {
this.left = left;
this.right = right;
}
public boolean evaluate( VariableValues values) {
return left.evaluate(values) || right.evaluate(values);
}
}
public class AndExpression implements Expression {
private final Expression left, right;
public AndExpression( Expression left, Expression right) {
this.left = left;
this.right = right;
}
public boolean evaluate( VariableValues values) {
return left.evaluate(values) && right.evaluate(values);
}
}
public interface Atom {
double evaluate( VariableValues values);
}
public class Literal implements Atom {
private final double value;
public Literal( double value) {
this.value = value;
}
public double evaluate( VariableValues values) {
return value;
}
}
public class Variable implements Atom {
private final String name;
public Variable( String name) {
this.name = name;
}
public double evaluate( VariableValues values) {
return values.getValue( name);
}
}
Here is one written in JavaScript: http://silentmatt.com/parser3.js (from: Javascript parser for simple expression). It will be a pain to port, but it's a start.
There is an implementation of a subset of JavaScript available in C# here at CodeProject.
I did something like this in flash/actionscrpt. I assume it's pretty much the same in Silverlight. After a quick google search I see that RegisterScriptableObject is similar to ActionScript's ExternalInterface. Anyway, here is my example.
This Silverlight example comes pretty close to doing the same thing.
精彩评论