jep学习文档

更新时间:2024-04-02 08:29:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

JEP(Java Expression Parser)学习笔记

By shenweifeng

2012-9-25 in HangZhou

JEP

基本用法

1、 入门 2、 错误处理 3、 计算表达式

4、 替代的解析和计算方法 5、 缩略的乘法

入门

在项目中使用JEP的类包是很简单的!下面的步骤将会使用很快入门。 1、 下载JEP 包,下载格式为ZIP 2、 解压缩该包

3、 移动jep-x.x.x.jar到你选择的目录中

4、 重要:为了当编译你的程序时Java编译器能找到jep类,编译器需要知道jep的位

置。所以你需要添加.jar文件的路径到你的CLASSPATH环境变量中。(如果你不清楚如何使用,请读这里);你的CLASSPATH变量应该包含某些东西如

C:\\java\\packages\\jep-x.x.x.jar,取决于你jar包放置的位置。或者,你也许需要在你的JAVA IDE中设置jar包的位置,使编译器能找到类库。

5、 在你的程序中,创建一个解析器对象,如:

org.nfunk.jep.JEPmyParser = new org.nfunk.jep.JEP();

6、 添加标准函数和常量,如果你想有能力使用三角函数和pi,e计算表达式。 myParser.addStandardFunctions(); myParser.addStandardConstants();

7、 通常情况下,你需要具体指明哪些变量能在表达式中被使用。如果一个变量在表达式被

解析时还没被添加,解析器将声明表达式是不合法的(这种情况能被改变通过使用 允许未声明的变量)。 添加变量x并且初始化为0, myParser.addVariable(\;

8、 解析表达式并计算它

myParser.parseExpression(ExpressionString); result = myParser.getValue();

9、 变量值可以通过addVariable(String, double)改变

样本小应用程序提供了更多更广泛的表现来展示解析器的使用。

错误处理

错误可能发生在解析表达式过程中,也可能发生在计算表达式过程中。方法handError()将报告一个错误是否已经发生在最近的行为中(或解析,或计算)。如果返回值是true,你能使用getErrorInfo()方法获得已发生错误的进一步错误信息。

计算表达式

4个计算表达式的方法是可以获得的:

? getValue(): 当你期望一个正常的浮点运算值时使用 ? getComplexValue():当你期望一个复数时使用

? getValueAsObject():返回结果为一个对象(任意类型)

? evaluate(Node root):从根节点计算一颗树(一种数据结构)并且返回一个对象。

前两种方法内在也算是getValueAsObject(),通过一些必须地转换就能变成double类型或者Complex类型。

替代的解析和计算方法

有一种替代方法解析和计算表达式,该方法为计算机应用提供了更多的灵活性,特别是在处理一系列相关的等式时。它们是:

public Node parse(String expression) throws ParseException public Object evaluate(Node node) throws Exception 分别解析和计算表达式。这些能被使用如下: try { 量 }

j.evaluate(node1);

Node node2 = j.parse(\Object val2 = j.evaluate(node2); System.out.println(\// Alternative syntax

Node node1 = j.parse(\实际使用中需要打开允许赋值和使用未声明变

catch(ParseException e) { }

catch(Exception e) }

因此,它更容易记录一系列等式。注意使用这种语法,异常需要被捕获而且getTopNode() 方法将不返回有意义的值。

{

System.out.println(\System.out.println(\

缩略的乘法

你能使用缩略的乘法通过使用setImplicitMul(true)。

默认的设置是false(没有缩略乘法)。

缩略乘法允许表达式例如“2 x”能被解释为“2*x”。注意两个变量之间的空格是必须的使它们被解释为乘法。这同样适用于变脸后跟随一个数字。举个例子“y 3”被解释为“y*3”,但是y3被解释为单个名为y3的变量。如果一个数字位于变量之前,它们之间的空格是不需要的,缩略乘法照样有效。

变量

? ? ? ? ?

基础

允许未声明的变量 获得变量队列

赋值(Assignment) 变量观察者

基础

变量有两种处理方式可供选择: 1、 不允许未声明的变量(默认):在这种情况下,解释一个表示式前添加表达式中的变量

时必须的。添加通过使用addVariable()方法实现。

2、 允许未声明的方法:如果你允许未声明的变量,它们将在表达式被解析时自动地添加到

符号表(symbol table)中。

在一个表达式已经被解析后,变量值可以通过使用addVaribale()或setVariable()方法来进行更新。这样就允许使用不同的值对表达式进行重复计算。

一个变量的值可以通过使用getVarValue()方法进行查询。一个更进一步的方法是使用getVar() ,返回一个代表该变量的对象。

这里的变量可能是常量,即不能改变值。添加一个新的常量使用addConstant,添加一个新的变量使用addVariable;

符号表类(SymbolTable)存储代表变量的对象而不仅仅是它们的值。该类的get和put已经过时,不赞成使用。这导致一个运算上的细微提高。

允许未声明的变量

使其能够解析未声明的变量,使用方法setAllowUndeclared(true)。

默认的设置是false(不允许未声明的变量)。

如果你不清楚声明变量名将会出现在将要解析的表达式中,你可以使用setAllowUndeclare(true)。设置该选择后,解析表达式前将变量添加到表达式中将不是必须的了。当解析时如果一个新变量被发现,它被自动的添加到符号表中。看获得变量队列,你可以读到有关怎么访问这些变量的方式。

获得变量队列

在未声明对象允许的情况下解析表达式,变量和常量队列被添加进解析器中,可以通过方法 getSymbolTable获得。该方法是最有效的当未声明变量选项被选中后。方法返回值是一个SymbolTable对象。注意SymbolTable继承HashTable。

赋值(Assignment)

赋值允许变量的值被设置通过使用等式中的“=”操作符,这样,执行操作例如x=3,

Y=x^2,然后y将获得值9。赋值能被使用通过标准的JEP包,没有需要用到org.lsmp.djep树中的任何包。为了打开赋值功能,JEP的方法setAllowAssignment(true)需要调用。 // standard initialisation JEP j = new JEP(); j.addStandardConstants(); j.addStandardFunctions(); j.addComplex(); j.setAllowUndeclared(true); j.setImplicitMul(true); // switch assignment facilities on j.setAllowAssignment(true); // parse assignment equations j.parseExpression(\// evaluate it - no need to save the value returned j.getValueAsObject(); // parse a second equation j.parseExpression(\j.getValueAsObject(); // an equation involving above variables j.parseExpression(\Object val3 = j.getValueAsObject(); System.out.println(\ 变量观察者

符号表(Symbol)和变量(Variable)都实现了观察者模式。这允许对象被通知当一个新的变量被创建或者值被改变。这里有个使用的例子:

public class MyObserver implements Observer {

public void initialise() {

SymbolTablest = j.getSymbolTable(); st.addObserver(this);

st.addObserverToExistingVariables(this); }

public void update(Observable arg0, Object arg1) {

if(arg0 instanceof Variable) println(\

else if(arg0 instanceofSymbolTable.StObservable) {

println(\

// This line is vital to ensure that

// any new variable created will be observed. ((Variable) arg1).addObserver(this); } }

}

数据类型

JEP支持大量的数据类型。这些包括数字(numbers),字符串(strings),矢量(vectors),复数(Complex numbers)。Numbers默认的表现方式是double类型。然而它也是可以改变表现形式的正如小节使用自定义的number类。

Strings

Strings能通过使用双引号加入到表达式中。它们能被连接通过使用“+”运算符,也能进行比较通过使用“==”和“!=”运算符。一个包含字符串的样例表达式是“foo”+“bar”==“foobar”,将被JEP计算为1(true);

添加字符串变量,使用方法addVariabe(String name,Object value)。如果一个表达式的结果是一个字符串,该值可以通过调用方法getValueAsObject()获得。

Vectors

Vectors被要求为Double元素的一个集合。一个Vector是一系列的值,通过“,“分隔,使用方括号”[“,”]“包围。一个包含Vector的样例为:

[3, 4, 5]*2

它将被JEP计算为[6,8,10]。添加一个vector为变量使用方法addVariable(String name,Object Value)。如果一个表达式的结果为一个vector,可以通过调用方法getValueAsObject()获得。

Complex numbers

如果你想在表达式中使用Complex numbers,你要在创建了解析器对象后调用addComplex()方法。这将添加一个虚数单位i。2个解析器函数re()和im()也被添加,可以使用它们获取复数的实数部分和虚数部分。你需要去导入org.nfunk.jep.type.Complex才能操作表达式中的复数。

通过使用虚数单位i,你可以操作表达式中的复数。一个简单的例子是(1+2*i)^3。当前 (re,im)标记是不支持的。

从表达式中获取一个复数,你需要使用getComplexValue()方法。它将进行运算并返回一个

Complex对象。注意这个类不仅实现了Complex numbers的运算,而且用来返回运算结果。

在运算前,通过使用addVariable(String name,doublere,doubleim)可以添加一个复数变量或常量到解析器中。它包含三个参数:变量的名称,实数部分和虚数部分。

使用自定义的Number类

默认的当一个表达式例如“1+2“被解析,常量1和2在内部将会被解析为Double对象。在大多数情况下这是好的。但是在某些情况下,你也许想要使用自定义的类来作为表现形式。 这是可行的通过创建一个实现了NumberFactory借口的Number类。它包含了一个方法 createNumber(String value),该方法将返回一个初始化参数值的对象。你能加载你的自定义number factory使用JEP构造器JEP(booleantraverse_in, Boolean allowUndeclared_in, Boolean implicitMul_in, NumberFactorynumberFactory_in)。 自定义的对象需要操作通过自定义的函数,你可以创建参考下面的使用说明,位于自定义函数页面。

自定义的类型

在大多数情况下,你只需要使用JEP提供的已存在的少量的类型(Double,Complex,Vector,String)。但是假如你需要计算包含其他类型的表达式。这是可行的通过使用addVariable(String name,Object value)去添加任意类型的变量。一个变量类型唯一有关的地方在函数类中。 当一个表达式被计算,变量的值被类中的方法操作。这些包括操作(加法和减法),同样还有函数Sine和Cosine。不修改源码的情况下,只有默认类型能被这些类操作。所以,为了有能力操作自己的类型,你需要去修改这些类,或者编写你自己的函数类依照在自定义函数小节中描述的。

运算符

内嵌的运算符

所有常用的算术操作都被支持。Boolean操作也都被支持。Boolean表达式计算后为1或0(分别代表真和假)。

符号表示该运算符能被指定对象的使用。关于运算符优先级可以参考语法部分获取详细信息。

Power Boolean Not ^ ! Double Complex String Vector (only +) Unary Plus, Unary Minus +x, -x Modulus Division Multiplication Addition, Subtraction Less or Equal, More or Equal % / * +, - <=, >= Less Than, Greater Than <, > Not Equal, Equal Boolean And Boolean Or !=, == && || 新增运算符

当前增加运算法的唯一方法就是修改JEP和源码。为一个解析器添加运算符一下几步是需要的:

? 创建一个新的标记,含描述名称,说明,一个代表该操作的字符串。 ? 修改语法规则,包含一个新运算符的规则。

? 创建一个新的对象实现了org.nfunk.jep.function.PostfixMathCommandI

接口,提供操作运算符的方法。

? 修改或替换org.nfunk.jep.OperatorSet绑定解析器中的运算符到

PostFixMathCommand

在尝试添加一个运算符之前,你应该熟悉如何添加一个函数到JEP中。

JavaCCThe Parse generator

解析器通过使用JavaCC解析/扫描生成器创建而不是通过手写的代码。生成器读取文件Parser.jjt(使用特殊的语言编写,定义了语法),生成Parser.java和其他一些实现Parser的类文件。

在尝试修改解析器之前,你需要读一些关于JavaCC和JJTree的文档。 生成解析器有三个过程: 1、 JJTree处于运行状态。它读取文件Parser.jjt,然后创建一个文件Parser.jj。

这一步的目的是为了添加需要的代码用来操作抽象语法树(树的结构代表了一个数学表达式)。创建parser.jj的同时也创建了一些java文件:Node.java代表解析器中的基础节点。ASTConstant.java一个代表常量“0”或“1”的节点;ASTVarNode。Java一个代表变量的节点。

2、

Javacc处于运行状态。它读取Parser.jj并且生成Parser.java,真正的实现Parser接口的代码。Parser.java是一个非常复杂的文件。几乎包含了2000行代码。

3、 正常的java编译步骤编译Parser.java

当项目通过使用build.xml文件构建时这个过程应该被自动的进行。简单的再次编译所有的java文件时不够的。

只有Parser.jjt文件可以被修改,Parser.jj,Parser.java不应该被修改,因为他们将被复写在构建过程中。更深一步,ASTConstant.java, ASTFunNode.java, ASTStart.java, ASTVarNode.java, JavaCharStream.java, JJTParserState.java, Node.java, SimpleNode.java, ParseException.java, ParserConstants.java, ParserTokenManager.java, ParseTreeConstants.java, Token.java, TokenMgrError.java也不应该被修改因为它们也是自动生成的。

函数

注意你总是能添加新的自定义函数。下面的每一个函数都能被运用到打的对象中。 函数的添加使用方法addStandardFuncions(); Sine Cosine Tangent Arc Sine Arc Cosine Arc Tangent Arc Tangent (with 2 parameters) Hyperbolic Sine sin(x) cos(x) tan(x) asin(x) acos(x) atan(x) atan2(y, x) sinh(x) Double Complex String Vector Hyperbolic Cosine cosh(x) Hyperbolic Tangent Inverse Hyperbolic Sine Inverse Hyperbolic Cosine Inverse Hyperbolic Tangent tanh(x) asinh(x) acosh(x) atanh(x) Natural Logarithm ln(x) Logarithm base 10 log(x) Exponential (e^x) exp(x) Absolute Value / Magnitude Random number (between 0 and 1) Modulus Square Root Sum If Str (number to string) Binomial coefficients Real Component abs(x) rand() mod(x,y) = x % y sqrt(x) sum(x,y,z) if(cond,trueval,falseval) str(x) binom(n,i) Integer values Vector 函数添加使用addComplex(): re(c) Double Complex String Imaginary Component im(c) Complex Modulus (Absolute Value) Argument (Angle of complex value, in radians) Complex conjugate Complex, constructs a complex number from complex(x,y) real and imaginar parts Polar, constructs a complex number from polar(r,theta) modulus and argument conj(c) arg(c) cmod(c) 自定义函数

下面是一个自定义函数被添加的例子。

假定你想要添加一个函数“half”用来取一个值的一半(演示目的)。

1、 创建一个继承PosfixMathCommand(位于org.nfunk.jep.function包中)的类。在这个例子

中,我们给它取名“Half”.

2、 在构造器中设置获取的参数的数量。在我们的例子中,个数为1.完成这个输入

本文来源:https://www.bwwdw.com/article/qdhr.html

Top