/***************************************************************
@header@
@date@
@copyright@
@license@
This file contains the grammar from which the parser is
generated. The parser generation tools being used are JavaCC
and its component JJTree. JJTree processes the .jjt file and
generates the .jj file. This file is then used be JavaCC to
generate the final java classes.
Knowledge of parser generation is definitely beneficial if you
are planning to make changes to this file. Although many parts
are self explanatory, it is helpful to know how the grammar is
handled by JJTree and JavaCC.
You can follow the structure of the grammar by starting in the
line which reads "ASTStart Start() #Start : {}". From there you
will be able to trace the same path the parser follows when
parsing an expression.
The concept of precedence is very important. Since this is a
recursive descent parser, it is easy to see which operators have
higher order of precedence. The following table outlines the
order incorporated in this grammar.
Order of precedence (from lowest to highest):
|| or (logical)
&& and (logical)
== != equal, not equal
< > <= >= less, greater, less or equal, greater or equal
+ - addition, subtraction
* / % multiplication, division, modulus
! + - not, unary +/-
^ power
The order of precedence is greatly inspired by the operator
precedence of the C language. See "The C Programming Language"
Kernighan & Richie 2nd Ed p.53. Java also uses a very similar
precedence. Note that the '^' operator is used for bitwise
exclusive OR in Java and C, but represents the power operator
in this grammar.
***************************************************************/
/***************************************************************
OPTIONS
***************************************************************/
options {
JAVA_UNICODE_ESCAPE = true;
MULTI = true;
VISITOR = true;
VISITOR_EXCEPTION = "ParseException";
NODE_DEFAULT_VOID = true;
// NODE_PACKAGE = "org.nfunk.jep.parser";
// BUILD_NODE_FILES=false;
STATIC = false;
// DEBUG_TOKEN_MANAGER = true;
// DEBUG_PARSER = true;
// DEBUG_LOOKAHEAD = true;
}
/***************************************************************
PARSER BEGIN
***************************************************************/
PARSER_BEGIN(Parser)
package org.nfunk.jep;
import java.util.Vector;
import org.nfunk.jep.function.*;
import org.nfunk.jep.type.*;
public class Parser {
private JEP jep;
private SymbolTable symTab;
private OperatorSet opSet;
private int initialTokenManagerState = DEFAULT;
public Node parseStream(java.io.Reader stream, JEP jep_in)
throws ParseException {
restart(stream,jep_in);
// Parse the expression, and return the
enable_tracing();
try
{
Node node = Start();
if (node == null) throw new ParseException("No expression entered");
return node.jjtGetChild(0);
}
catch(TokenMgrError e) {
throw new ParseException(e.getMessage());
}
}
/**
* Restart the parse with the given stream.
* @since 2.3.0 beta 1
*/
public void restart(java.io.Reader stream, JEP jep_in)
{
ReInit(stream);
this.token_source.SwitchTo(initialTokenManagerState);
jep = jep_in;
symTab = jep.getSymbolTable();
opSet = jep.getOperatorSet();
}
/**
* Continue parsing without re-initilising stream.
* Allows reentrance of parser so that strings like
* "x=1; y=2; z=3;" can be parsed.
* When a semi colon is encountered parsing finishes leaving the rest of the string unparsed.
* Parsing can be resumed from the current position by using this method.
* For example
*
* XJep j = new XJep();
* Parser parse = j.getParse();
* StringReader sr = new StringReader("x=1; y=2; z=3;");
* parse.restart(sr,j);
* Node node;
* try {
* while((node = j.continueParse())!=null) {
* j.println(node);
* } }catch(ParseException e) {}
*
*/
public Node continueParse() throws ParseException
{
try
{
Node node = Start();
if (node == null) return null;
return node.jjtGetChild(0);
}
catch(TokenMgrError e) {
throw new ParseException(e.getMessage());
}
}
private void addToErrorList(String errorStr) {
jep.errorList.addElement(errorStr);
}
/**
* Sets the initial state that the token manager is in.
* Can be used to change how x.x is interpreted, either as a single
* identifier (DEFAULT) or as x x (NO_DOT_IN_IDENTIFIERS)
* @param state the state to be in. Currently the only legal values are DEFAULT and NO_DOT_IN_IDENTIFIER
*/
public void setInitialTokenManagerState(int state)
{
initialTokenManagerState = state;
}
/**
* Translate all escape sequences to characters. Inspired by Rob Millar's
* unescape() method in rcm.util.Str fron the Web Sphinx project.
*
* @param inputStr String containing escape characters.
* @return String with all escape sequences replaced.
*/
private String replaceEscape(String inputStr) {
int len = inputStr.length();
int p = 0;
int i;
String metachars = "tnrbf\\\"'";
String chars = "\t\n\r\b\f\\\"'";
StringBuffer output = new StringBuffer();
while ((i = inputStr.indexOf('\\', p)) != -1) {
output.append(inputStr.substring(p, i));
if (i+1 == len) break;
// find metacharacter
char metac = inputStr.charAt(i+1);
// find the index of the metac
int k = metachars.indexOf(metac);
if (k == -1) {
// didn't find the metachar, leave sequence as found.
// This code should be unreachable if the parser
// is functioning properly because strings containing
// unknown escape characters should not be accepted.
output.append('\\');
output.append(metac);
} else {
// its corresponding true char
output.append(chars.charAt(k));
}
// skip over both escape character & metacharacter
p = i + 2;
}
// add the end of the input string to the output
if (p < len)
output.append(inputStr.substring(p));
return output.toString();
}
}
PARSER_END(Parser)
/***************************************************************
SKIP
***************************************************************/
<*> SKIP :
{
" "
| "\t"
| "\n"
| "\r"
| <"//" (~["\n","\r"])* ("\n"|"\r"|"\r\n")>
| <"/*" (~["*"])* "*" (~["/"] (~["*"])* "*")* "/">
}
/***************************************************************
TOKENS
***************************************************************/
<*> TOKEN : /* LITERALS */
{
< INTEGER_LITERAL:
>
|
< #DECIMAL_LITERAL: ["0"-"9"] (["0"-"9"])* >
|
< FLOATING_POINT_LITERAL:
(["0"-"9"])+ "." (["0"-"9"])* ()?
| "." (["0"-"9"])+ ()?
| (["0"-"9"])+
>
|
< #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
|
< STRING_LITERAL:
"\""
( (~["\"","\\","\n","\r"])
| ("\\" ["n","t","b","r","f","\\","'","\""] )
)*
"\""
>
}
/* IDENTIFIERS
Letters before version 2.22
< #LETTER: ["_","a"-"z","A"-"Z"] >
In Ver 2.3.0.1 presence of . in an identifier is switchable.
In the DEFAULT lexical state identifiers can contain a .
In the NO_DOT_IN_IDENTIFIERS state identifiers cannot contain a .
the state can be set by using
Parser.setInitialTokenManagerState
*/
TOKEN:
{
(||".")*>
|
< #LETTER1:
[
"\u0024", // $
"\u0041"-"\u005a", // A - Z
"\u005f", // _
"\u0061"-"\u007a", // a - z
"\u00c0"-"\u00d6", // Upper case symbols of Latin-1 Supplement
"\u00d8"-"\u00f6", // Lower case symbols of Latin-1 Supplement
"\u00f8"-"\u00ff", // More lower case symbols of Latin-1 Supplement
"\u0100"-"\u1fff", // Many languages (including Greek)
"\u3040"-"\u318f", // Hiragana, Katakana, Bopomofo, Hangul Compatibility Jamo
"\u3300"-"\u337f", // CJK Compatibility
"\u3400"-"\u3d2d", // CJK Unified Ideographs Extension A
"\u4e00"-"\u9fff", // CJK Unified Ideographs
"\uf900"-"\ufaff" // CJK Compatibility Ideographs
]
>
|
< #DIGIT1: ["0"-"9"] >
}
TOKEN:
{
(|)*>
|
< #LETTER2:
[
"\u0024", // $
"\u0041"-"\u005a", // A - Z
"\u005f", // _
"\u0061"-"\u007a", // a - z
"\u00c0"-"\u00d6", // Upper case symbols of Latin-1 Supplement
"\u00d8"-"\u00f6", // Lower case symbols of Latin-1 Supplement
"\u00f8"-"\u00ff", // More lower case symbols of Latin-1 Supplement
"\u0100"-"\u1fff", // Many languages (including Greek)
"\u3040"-"\u318f", // Hiragana, Katakana, Bopomofo, Hangul Compatibility Jamo
"\u3300"-"\u337f", // CJK Compatibility
"\u3400"-"\u3d2d", // CJK Unified Ideographs Extension A
"\u4e00"-"\u9fff", // CJK Unified Ideographs
"\uf900"-"\ufaff" // CJK Compatibility Ideographs
]
>
|
< #DIGIT2: ["0"-"9"] >
}
/* OPERATORS */
<*> TOKEN:
{
< ASSIGN:"=" > // rjm
| < SEMI: ";" > // rjm
| < COMMA: "," > // rjm
| < GT: ">" >
| < LT: "<" >
| < EQ: "==" >
| < LE: "<=" >
| < GE: ">=" >
| < NE: "!=" >
| < AND: "&&" >
| < OR: "||" >
| < PLUS: "+" >
| < MINUS:"-" >
| < MUL: "*" >
| < DOT: "." > // rjm
| < DIV: "/" >
| < MOD: "%" >
| < NOT: "!" >
| < POWER:"^" >
| < CROSS:"^^" > // rjm
| < LSQ: "[" > // rjm
| < RSQ: "]" > // rjm
| < LRND: "(" > // rjm
| < RRND: ")" > // rjm
| < COLON: ":" > // rjm
}
/***************************************************************
GRAMMAR START
***************************************************************/
ASTStart Start() #Start :
{
}
{
Expression() ( | ) { return jjtThis; }
| ( | )
{
// njf - The next line is commented out in 2.3.0 since
// two "No expression entered" errors are reported
// in EvaluatorVisitor and Console (one from here
// the other from ParseStream() )
// Decided to just return null, and handle the error
// in ParseStream.
// addToErrorList("No expression entered");
return null;
}
}
// Expresions can be like
// x=3
// x=y=3 parsed as x=(y=3)
void Expression() : {}
{
LOOKAHEAD(LValue() ) // need to prevent javacc warning with left recusion
AssignExpression() // rjm changes from OrExpresion
|
RightExpression()
}
void AssignExpression() : {} // rjm addition
{
( LValue() Expression()
{
if (!jep.getAllowAssignment()) throw new ParseException(
"Syntax Error (assignment not enabled)");
jjtThis.setOperator(opSet.getAssign());
}
)
#FunNode(2)
}
void RightExpression() :
{
}
{
OrExpression()
}
void OrExpression() :
{
}
{
AndExpression()
(
( AndExpression()
{
jjtThis.setOperator(opSet.getOr());
}
) #FunNode(2)
)*
}
void AndExpression() :
{
}
{
EqualExpression()
(
( EqualExpression()
{
jjtThis.setOperator(opSet.getAnd());
}
) #FunNode(2)
)*
}
void EqualExpression() :
{
}
{
RelationalExpression()
(
( RelationalExpression()
{
jjtThis.setOperator(opSet.getNE());
}
) #FunNode(2)
|
( RelationalExpression()
{
jjtThis.setOperator(opSet.getEQ());
}
) #FunNode(2)
)*
}
void RelationalExpression() :
{
}
{
AdditiveExpression()
(
( AdditiveExpression()
{
jjtThis.setOperator(opSet.getLT());
}
) #FunNode(2)
|
( AdditiveExpression()
{
jjtThis.setOperator(opSet.getGT());
}
) #FunNode(2)
|
( AdditiveExpression()
{
jjtThis.setOperator(opSet.getLE());
}
) #FunNode(2)
|
( AdditiveExpression()
{
jjtThis.setOperator(opSet.getGE());
}
) #FunNode(2)
)*
}
void AdditiveExpression() :
{
}
{
MultiplicativeExpression()
(
( MultiplicativeExpression()
{
jjtThis.setOperator(opSet.getAdd());
}
) #FunNode(2)
|
( MultiplicativeExpression()
{
jjtThis.setOperator(opSet.getSubtract());
}
) #FunNode(2)
)*
}
void MultiplicativeExpression() :
{
}
{
UnaryExpression()
(
(
PowerExpression()
{
if (!jep.implicitMul) throw new ParseException(
"Syntax Error (implicit multiplication not enabled)");
jjtThis.setOperator(opSet.getMultiply());
}
) #FunNode(2)
|
( UnaryExpression()
{
jjtThis.setOperator(opSet.getMultiply());
}
) #FunNode(2)
|
( UnaryExpression()
{
jjtThis.setOperator(opSet.getDot());
}
) #FunNode(2)
|
( UnaryExpression()
{
jjtThis.setOperator(opSet.getCross());
}
) #FunNode(2)
|
( UnaryExpression()
{
jjtThis.setOperator(opSet.getDivide());
}
) #FunNode(2)
|
(
UnaryExpression()
{
jjtThis.setOperator(opSet.getMod());
}
) #FunNode(2)
)*
}
void UnaryExpression() :
{
}
{
( UnaryExpression())
|
( UnaryExpression()
{
jjtThis.setOperator(opSet.getUMinus());
}
) #FunNode(1)
|
( UnaryExpression()
{
jjtThis.setOperator(opSet.getNot());
}
) #FunNode(1)
|
PowerExpression()
}
void PowerExpression() :
{
}
{
UnaryExpressionNotPlusMinus()
[
( UnaryExpression()
{
jjtThis.setOperator(opSet.getPower());
}
) #FunNode(2)
]
}
void UnaryExpressionNotPlusMinus() :
{
String identString = "";
int type;
}
{
AnyConstant()
|
LOOKAHEAD(ArrayAccess())
ArrayAccess()
|
LOOKAHEAD({ (getToken(1).kind == INDENTIFIER1 || getToken(1).kind == INDENTIFIER2) &&
jep.funTab.containsKey(getToken(1).image) })
Function()
|
Variable()
|
Expression()
|
// LOOKAHEAD( Expression() )
// RangeExpression()
// |
ListExpression()
}
void ListExpression() #FunNode:
{
jjtThis.setOperator(opSet.getList());
}
{
Expression() ( Expression() )*
}
/*
void RangeExpression() #FunNode:
{
jjtThis.setOperator(opSet.getRange());
}
{
Expression() ( Expression() )+
}
*/
void LValue() :
{
}
{
LOOKAHEAD(ArrayAccess())
ArrayAccess()
| Variable()
}
void ArrayAccess() :
{
}
{
Variable() ListExpression()
{
jjtThis.setOperator(opSet.getElement());
} #FunNode(2)
}
void Variable() :
{
String identString = "";
}
{
(identString = Identifier()
{
if (symTab.containsKey(identString)) {
jjtThis.setVar(symTab.getVar(identString));
} else {
if (jep.allowUndeclared) {
jjtThis.setVar(symTab.makeVarIfNeeded(identString));
} else {
addToErrorList("Unrecognized symbol \"" + identString +"\"");
}
}
}
) #VarNode
}
void Function() :
{
int reqArguments = 0;
String identString = "";
}
{
( identString = Identifier()
{
if (jep.funTab.containsKey(identString)) {
//Set number of required arguments
reqArguments =
((PostfixMathCommandI)jep.funTab.get(identString)).getNumberOfParameters();
jjtThis.setFunction(identString,
(PostfixMathCommandI)jep.funTab.get(identString));
} else {
addToErrorList("!!! Unrecognized function \"" + identString +"\"");
}
}
ArgumentList(reqArguments, identString)
) #FunNode
}
void ArgumentList(int reqArguments, String functionName) :
{
int count = 0;
String errorStr = "";
}
{
[
Expression() { count++; }
(
Expression() { count++; }
)*
]
{
if(reqArguments == -1) {
if(!((PostfixMathCommandI)jep.funTab.get(functionName)).checkNumberOfParameters(count))
{
errorStr = "Function \"" + functionName +"\" illegal number of arguments " + count;
addToErrorList(errorStr);
}
}
else if (reqArguments != count) {
errorStr = "Function \"" + functionName +"\" requires "
+ reqArguments + " parameter";
if (reqArguments!=1) errorStr += "s";
addToErrorList(errorStr);
}
}
}
String Identifier() :
{
Token t;
}
{
( t = | t = ) { return t.image; }
}
void AnyConstant() #Constant:
{
Token t;
Object value;
}
{
t= {
// strip away double quotes at end of string
String temp = (t.image).substring(1,t.image.length()-1);
// replace escape characters
temp = replaceEscape(temp);
jjtThis.setValue(temp);
}
|
value = RealConstant() {
jjtThis.setValue(value);
// }
// |
// value = Array() {
// jjtThis.setValue(value);
}
}
/*
Vector Array() :
{
Object value;
Vector result = new Vector();
}
{
value = RealConstant()
{
result.addElement(value);
}
(
value = RealConstant()
{
result.addElement(value);
}
)*
{
return result;
}
}
*/
Object RealConstant() :
{
Token t;
Object value;
}
{
(t= | t=)
{
try {
value = jep.getNumberFactory().createNumber(t.image);
} catch (Exception e) {
value = null;
addToErrorList("Can't parse \"" + t.image + "\"");
}
return value;
}
}