/** The grammar for a mechanism description
* The format is BNF-like, as used by JavaCC
*
*/
options {
STATIC = false;
}
PARSER_BEGIN(MechanismParser)
public class MechanismParser{}
PARSER_END(MechanismParser)
////////////////// whitespace and line separators
SKIP: { /* white space and comments */
" " | "\n" | "\r" | "\t" | "\f"
| "//" : IN_SINGLE_LINE_COMMENT
| "/*" : IN_MULTILINE_COMMENT
}
<IN_SINGLE_LINE_COMMENT> SKIP: { < "\n" | "\r" > : DEFAULT | <~[]> }
<IN_MULTILINE_COMMENT> SKIP: { "*/" : DEFAULT | <~[]> }
TOKEN: { < REACTION_MARKER: "<->" > }
TOKEN: { /* binary operators */
< IF: "?" >
| < OR: "|" >
| < AND: "&" >
| < EQ: "==" >
| < LE: "<=" >
| < GE: ">=" >
| < NE: "!=" >
| < LT: "<" >
| < GT: ">" >
| < ADD: "+" >
| < SUB: "-" >
| < MUL: "*" >
| < DIV: "/" >
| < MOD: "%" >
| < POW: "^" >
}
TOKEN: { /* reserved words */
< RATE: "rate(" > // Note: no space before the '('
| < RATECONSTANT: "k(" >
| < FINAL: "final" >
| < OUTPUT_MARKER: "*output" >
| < SCRIPT_MARKER: "*script" > : IN_SCRIPT
}
<DEFAULT, IN_SCRIPT>
TOKEN: {
< INT: ["1"-"9"] (["0"-"9"])* > // integers
}
TOKEN: { /* numbers--from the Java grammar */
< NUMBER:
(<DIGIT>)+ ( "." (<DIGIT>)* (<EXPONENT>)? )?
| "." (<DIGIT>)+ (<EXPONENT>)?
| (<DIGIT>)+ <EXPONENT>
>
| < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
}
<DEFAULT, IN_SCRIPT>
TOKEN: { /* identifier--Unicode from the Java grammar; allows ' in names */
< ID: <LETTER> (<LETTER>|<DIGIT>|"'")* >
| < #LETTER: ["A"-"Z","a"-"z","$","_"] >
| < #DIGIT: ["0"-"9"] >
}
TOKEN: { // java identifier; starts with @ and allows dots to separate parts of names
< JAVAID: "@" <LETTER> (<LETTER>|<DIGIT>|".")* >
}
// string definition from the Java grammar.
<DEFAULT, IN_SCRIPT>
TOKEN: {
< STRING:
"\"" (
(~["\"","\\","\n","\r"])
| "\\" (
["n","t","b","r","f","\\","'","\""]
| ["0"-"7"] ( ["0"-"7"] )?
| ["0"-"3"] ["0"-"7"] ["0"-"7"]
)
)* "\""
>
}
////////////////// Mechanism grammar
void mechanism(): {}{
[ reactionBlock() ]
[ outputBlock() ]
[ scriptBlock() ]
<EOF>
}
void reactionBlock() : {}{
( [
LOOKAHEAD (reactionLookahead()) reaction()
| expression()
] eol() )+
}
void outputBlock() : {}{
<OUTPUT_MARKER>
( [ expression() ] eol() )*
}
void scriptBlock() : {}{
<SCRIPT_MARKER>
script ()
}
void eol(): {}{ ";" }
////////////////// Reaction grammar
// used for lookahead to decide if a statement is a reaction; don't
// need to look for the whole thing, just to the <REACTION_MARKER>
void reactionLookahead() : {}{
side() <REACTION_MARKER>
}
void reaction() : {}{
side() ( <REACTION_MARKER> side() )+
}
void side() : {}{
reactant() ( <ADD> reactant() )*
}
void reactant() : {}{
[ <INT> ] <ID>
}
////////////////// Expression grammar
void expression(): {}{
assignExpression()
| <FINAL> t = <ID> ":" expression()
}
void assignExpression(): {}{
binaryExpression() [
"=" assignExpression()
| ":" assignExpression()
]
}
// This part of the grammar will determine whether a mechanism is well-formed or not,
// but does not parse the precedence of operators correctly. The actual grammar uses
// the precedence-climbing algorithm described by
// Theodore Norvell
void binaryExpression (): {}{
unaryExpression() ( op=binary() binaryExpression () )*
}
void binary(): {}{
<IF>|<OR>|<AND>|<EQ>|<LE>|<GE>|<NE>|<LT>|<GT>|
<ADD>|<SUB>|<MUL>|<DIV>|<MOD>|<POW>
}
// unary expressions have higher precedence than any binary
// consistent, I believe, with Java but not with standard algebraic use
// so -x^2 parses as (-x)^2, not -(x^2)
void unaryExpression(): {}{
<ADD> unaryExpression()
// we allow no-parens functions [ exp x rather than exp(x) ]
| unary() unaryExpression()
| primaryExpression()
}
/* predefined unary operators. for any other method, use a <JAVAID> */
void unary(): {}{
<SUB> | "!" | "exp" | "ln"
}
void primaryExpression(): {}{
number()
| identifier()
| rate()
| rateConstant()
| javaID()
| "(" expression() ")"
}
void number(): {}{
<INT> | <NUMBER>
}
void identifier(): {}{
<ID>
}
void rate(): {}{
<RATE> (
reactionNumber()
| <ID>
) ")"
}
void rateConstant(): {}{
<RATECONSTANT> reactionNumber() ")"
}
void reactionNumber(): {}{
( <ADD> | <SUB> ) [ <INT> ]
}
void javaID(): {}{
<JAVAID> [ "(" [ argumentList() ] ")" ]
}
void argumentList (): {}{
expression() ( "," expression() )*
}
////////////////// TOKENS
////////////////// Script processing
// This is a simplified view of a script: just any sequence of characters from the script marker to
// the end of the string.
// The real grammar scans the remaining text for specified strings (reserved words like go
// or variable names) that need to be modified to work with the mechanism interpreter.
// The rest is passed straight through to the BeanShell
// interpreter for parsing; that interpreter will decide if any errors exist in the script.
void script () : {}{
( ~[] )*
}