// This file is part of the program FRYSK. // Copyright 2005, 2007 Red Hat Inc. // FRYSK is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by // the Free Software Foundation; version 2 of the License. // FRYSK is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // You should have received a copy of the GNU General Public License // along with FRYSK; if not, write to the Free Software Foundation, // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. // In addition, as a special exception, Red Hat, Inc. gives You the // additional right to link the code of FRYSK with code not covered // under the GNU General Public License ("Non-GPL Code") and to // distribute linked combinations including the two, subject to the // limitations in this paragraph. Non-GPL Code permitted under this // exception must only link to the code of FRYSK through those well // defined interfaces identified in the file named EXCEPTION found in // the source code files (the "Approved Interfaces"). The files of // Non-GPL Code may instantiate templates or use macros or inline // functions from the Approved Interfaces without causing the // resulting work to be covered by the GNU General Public // License. Only Red Hat, Inc. may make changes or additions to the // list of Approved Interfaces. You must obey the GNU General Public // License in all respects for all of the FRYSK code and other code // used in conjunction with FRYSK except the Non-GPL Code covered by // this exception. If you modify this file, you may extend this // exception to your version of the file, but you are not obligated to // do so. If you do not wish to provide this exception without // modification, you must delete this exception statement from your // version and license this file solely under the GPL without // exception. grammar CExpr; options { output = AST; } @header { // This file is part of the program FRYSK. // // Copyright 2005, 2007 Red Hat Inc. // // FRYSK is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by // the Free Software Foundation; version 2 of the License. // // FRYSK is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with FRYSK; if not, write to the Free Software Foundation, // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. // // In addition, as a special exception, Red Hat, Inc. gives You the // additional right to link the code of FRYSK with code not covered // under the GNU General Public License ("Non-GPL Code") and to // distribute linked combinations including the two, subject to the // limitations in this paragraph. Non-GPL Code permitted under this // exception must only link to the code of FRYSK through those well // defined interfaces identified in the file named EXCEPTION found in // the source code files (the "Approved Interfaces"). The files of // Non-GPL Code may instantiate templates or use macros or inline // functions from the Approved Interfaces without causing the // resulting work to be covered by the GNU General Public // License. Only Red Hat, Inc. may make changes or additions to the // list of Approved Interfaces. You must obey the GNU General Public // License in all respects for all of the FRYSK code and other code // used in conjunction with FRYSK except the Non-GPL Code covered by // this exception. If you modify this file, you may extend this // exception to your version of the file, but you are not obligated to // do so. If you do not wish to provide this exception without // modification, you must delete this exception statement from your // version and license this file solely under the GPL without // exception. package frysk.expr; import java.util.ArrayList; import frysk.value.ArithmeticType; import frysk.value.SignedType; import frysk.value.UnsignedType; import frysk.value.FloatingPointType; import frysk.value.Value; import javax.naming.NameNotFoundException; import frysk.value.InvalidOperatorException; import frysk.value.OperationNotDefinedException; import inua.eio.ByteOrder; import lib.dwfl.BaseTypes; } @members { /** * A member variable to keep track of TAB completions requests. * If this is true the normal course of action is to simply * bail out by throwing an exception */ private boolean bTabPressed; private int assign_stmt_RHS_found; //private String sInputExpression; protected CppParser(TokenStream lexer, String sInput) { this(lexer); bTabPressed = false; //sInputExpression = sInput; } } // The start rule simply expects an expression list following by // the end of text symbol (ETX \3). // // The TabException propagates all the way up, to the start rule, // which then propagates it up to the calling program. start throws TabException : expressionList ETX ; /** * This rule looks for comma separated expressions. */ expressionList throws TabException : expression (COMMA! expression)* -> ^(EXPR_LIST expression) ; expression! throws TabException : assignment_expression // assign_expr1=assignment_expression // { // // ## = #assign_expr1; // ^($assign_expr1.tree); // } ; /** * Assignment expressions of the form "expr1 = expr2 = expr3". * Notice that the operator can be any assignment operator. */ assignment_operator : ASSIGNEQUAL | PLUSEQUAL | MINUSEQUAL | TIMESEQUAL | DIVIDEEQUAL | MODEQUAL | SHIFTRIGHTEQUAL | SHIFTLEFTEQUAL | BITWISEANDEQUAL | BITWISEXOREQUAL | BITWISEOREQUAL ; assignment_expression throws TabException : conditional_expression ( ( assignment_operator ) assignment_expression )? { if ($a.tree != null) ^($o.tree $c.tree $a.tree); else ^($c.tree); } ; /** * Conditional expressions of the form * (logical_expr)?expr:expr */ conditional_expression! throws TabException : log_or_expr=logical_or_expression (ques=QUESTIONMARK expr=assignment_expression colon=COLON cond_expr=conditional_expression)? { if (ques != null) //## = ^([COND_EXPR, "ConditionalExpression"], #log_or_expr, #expr, #cond_expr); ^(COND_EXPR $log_or_expr.tree $expr.tree $cond_expr.tree; else //## = #log_or_expr; ^($log_or_expr.tree); } ; logical_or_expression throws TabException : logical_and_expression (OR^ logical_and_expression)* ; logical_and_expression throws TabException : inclusive_or_expression (AND^ inclusive_or_expression)* ; inclusive_or_expression throws TabException : exclusive_or_expression (BITWISEOR^ exclusive_or_expression)* ; exclusive_or_expression throws TabException : and_expression (BITWISEXOR^ and_expression)* ; and_expression throws TabException : equality_expression (AMPERSAND^ equality_expression)* ; equality_expression throws TabException : relational_expression ((NOTEQUAL^ | EQUAL^) relational_expression)* ; relational_expression throws TabException : shift_expression ( ( LESSTHAN^ | GREATERTHAN^ | LESSTHANOREQUALTO^ | GREATERTHANOREQUALTO^ ) shift_expression )* ; shift_expression throws TabException : additive_expression ((SHIFTLEFT^ | SHIFTRIGHT^) additive_expression)* ; additive_expression throws TabException : multiplicative_expression ( (PLUS^ | MINUS^) multiplicative_expression )* ; multiplicative_expression throws TabException : unary_expression ( (STAR^ | DIVIDE^ | MOD^) unary_expression )* ; unary_expression throws TabException : PLUS^ unary_expression | MINUS^ unary_expression | PLUSPLUS^ postfix_expression | MINUSMINUS^ postfix_expression | TILDE^ unary_expression | NOT^ unary_expression | unary_expression_simple ; unary_expression_simple throws TabException : AMPERSAND prim_expr=id_expression { //## = ^([ADDRESS_OF, "Address Of"], #prim_expr); ^(ADDRESS_OF $prim_expr.tree); } | STAR mem_expr=id_expression { //## = ^([MEMORY, "Memory"], #mem_expr); ^(MEMORY $mem_expr.tree); } | cast_expression | postfix_expression ; primitiveType : 'boolean' | 'char' | 'byte' | 'short' | 'int' | 'long' | 'float' | 'double' ; cast_expression! throws TabException : LPAREN type=primitiveType RPAREN expr=unary_expression // | LPAREN (expression) RPAREN unary_expression_simple // | LPAREN (expression | primitiveType) RPAREN unary_expression_simple { //## = ^([CAST, "Cast"], #type, #expr); ^(CAST $type.tree $expr.tree); } ; postfix_expression throws TabException @init {String sTabText;} : post_expr1=primary_expression { if (bTabPressed) { // ??? Use antlr expressions instead of tree surgery. if (#post_expr1.getFirstChild() != null) if (#post_expr1.getFirstChild().getNextSibling() != null) sTabText = #post_expr1.getFirstChild().getNextSibling().getText(); else sTabText = #post_expr1.getFirstChild().getText(); else sTabText = #post_expr1.getText(); if (#post_expr1.getText().startsWith("Class Reference")) sTabText += "."; throw new TabException(#post_expr1, sTabText); } } ; /** * The TAB over here is not part of the C++ grammar. * This enables auto-completion by allowing the user * to press TAB whenever auto-completion is required */ primary_expression throws TabException : (TAB {bTabPressed = true;} | primary_identifier) | constant | 'this' | LPAREN! expression RPAREN! ; /** * TabException is raised everytime the TAB is pressed. * The parser thus bails out immediately and returns the * parse tree constructed so far. */ /* ??? add (id_expression | (TAB {bTabPressed = true;})) */ primary_identifier! throws TabException @init { AST astPostExpr = null, astDotExpr = null; } : ( prim_expr= id_expression { //astPostExpr = #prim_expr; ^($prim_expr.tree); } ( : LPAREN (expr2=expressionList)? RPAREN { //astPostExpr = ^([FUNC_CALL, "FuncCall"], #astPostExpr, #expr2); ^(FUNC_CALL $astPostExpr.tree $expr2.tree); } | LSQUARE arrExpr1=expression (COLON arrExpr2=expression)? RSQUARE // a[b][c] => (Array Reference a (Subscript b-lbound) // (Subscript b-hbound) (Subscript c-lbound)...) {AST sub = null; if (astPostExpr.getFirstChild() != null) { // #sub = ^(#[SUBSCRIPT,"Subscript"], #arrExpr1); ^(SUBSCRIPT $arrExpr1.tree); astPostExpr.addChild(#sub); // arr[n] is treated as arr[n:n] if (#arrExpr2 != null) #sub = ^(#[SUBSCRIPT,"Subscript"], #arrExpr2); else #sub = ^(#[SUBSCRIPT,"Subscript"], #arrExpr1); astPostExpr.addChild(#sub); } else { #sub = ^(#[SUBSCRIPT,"Subscript"], #arrExpr1); #astPostExpr = ^(#[REFERENCE,"Array Reference"], #astPostExpr, #sub); if (#arrExpr2 != null) #sub = ^(#[SUBSCRIPT,"Subscript"], #arrExpr2); else #sub = ^(#[SUBSCRIPT,"Subscript"], #arrExpr1); astPostExpr.addChild(#sub); } } // | AT at_expr=expression // // a@N => (Array Reference a (Subscript N) (Subscript N)) // {AST sub = null; // #sub = ^(#[SUBSCRIPT,"Subscript"], #[DECIMALINT,"0"]); // #astPostExpr = ^(#[REFERENCE,"Array Reference"], #astPostExpr, #sub); // // allow for 0 origin lower bound // #at_expr.setText(new String(Integer.toString(Integer.parseInt(#at_expr.getText()) - 1))); // #sub = ^(#[SUBSCRIPT,"Subscript"], #at_expr); // astPostExpr.addChild(#sub); // } | DOT! ( tb=TAB { bTabPressed = true; astDotExpr = #tb; } | id_expr1=id_expression { astDotExpr = #id_expr1;} ) // a.b.c => (Class Reference a b c)) {if (astPostExpr.getFirstChild() != null) { if (#astDotExpr.getText().endsWith("\t") == false) astPostExpr.addChild(#astDotExpr); } else { if (#astDotExpr.getText().endsWith("\t") == false) //#astPostExpr = ^(#[REFERENCE,"Class Reference"], #astPostExpr, #astDotExpr); ^(REFERENCE $astPostExpr.tree $astDotExpr.tree); else //#astPostExpr = ^(#[REFERENCE,"Class Reference"], #astPostExpr); ^(REFERENCE $astPostExpr.tree); } } | POINTERTO id_expr2=id_expression { //astPostExpr = ^(POINTERTO, #astPostExpr, #id_expr2); ^(POINTERTO $astPostExpr.tree $id_expr2.tree) } | PLUSPLUS { //astPostExpr = ^(PLUSPLUS, #astPostExpr); ^(CPLUSPLUS $astPostExpr.tree) } | MINUSMINUS { //astPostExpr = ^(MINUSMINUS, #astPostExpr); ^(MINUSMINUS $astPostExpr.tree) } )* ) { //## = #astPostExpr; ^($astPostExpr.tree) } ; constant : OCTALINT | DECIMALINT | HEXADECIMALINT | CharLiteral | (StringLiteral)+ | FLOAT | DOUBLE | 'true' | 'false' ; id_expression : IDENT ; tid_expression : TAB_IDENT ; /*---------------------------------------------------------------------------- * The Lexer *----------------------------------------------------------------------------*/ // @lexer::members { // OPERATOR = 'operator'; // } /* Operators: */ ASSIGNEQUAL : '=' ; COLON : ':' ; COMMA : ',' ; QUESTIONMARK : '?' ; SEMICOLON : ';' ; POINTERTO : '->'; ETX : '\3' ; LPAREN : '(' ; RPAREN : ')' ; LSQUARE : '[' ; RSQUARE : ']' ; LCURLY : '{' ; RCURLY : '}' ; AT : '@' ; EQUAL : '==' ; NOTEQUAL : '!=' ; LESSTHANOREQUALTO : '<=' ; LESSTHAN : '<' ; GREATERTHANOREQUALTO : '>=' ; GREATERTHAN : '>' ; DIVIDE : '/' ; DIVIDEEQUAL : '/=' ; PLUS : '+' ; PLUSEQUAL : '+=' ; PLUSPLUS : '++' ; MINUS : '-' ; MINUSEQUAL : '-=' ; MINUSMINUS : '--' ; STAR : '*' ; TIMESEQUAL : '*=' ; MOD : '%' ; MODEQUAL : '%=' ; SHIFTRIGHT : '>>' ; SHIFTRIGHTEQUAL : '>>=' ; SHIFTLEFT : '<<' ; SHIFTLEFTEQUAL : '<<=' ; AND : '&&' ; NOT : '!' ; OR : '||' ; AMPERSAND : '&' ; BITWISEANDEQUAL : '&=' ; TILDE : '~' ; BITWISEOR : '|' ; BITWISEOREQUAL : '|=' ; BITWISEXOR : '^' ; BITWISEXOREQUAL : '^=' ; fragment ELLIPSIS : '...' ; fragment DOT : '.' ; SCOPE : '::' ; fragment IDENT : ('$')*('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')* ; /** * A token is returned not only on regular tabs * but also when a TAB is hit after an incomplete variable */ fragment TAB : (IDENT)?'\t' ; TAB_IDENT : ((IDENT)'\t')=>TAB {$type = TAB;} | ('\t')=>TAB {$type = TAB;} | IDENT {$type = IDENT;} ; fragment NL : '\r' ('\n')? // DOS/Windows | '\n' // Unix { /* newline(); */ } ; WS : ( ' ' | '\f' | NL | '\\' ( '\r' ('\n')? | '\n' ) ) { $channel=HIDDEN; } ; CharLiteral : '\'' (Escape | ~( '\'' )) '\'' ; StringLiteral : '\"' ( Escape | ( '\\\r\n' // MS | '\\\r' // MAC | '\\\n' // Unix ) | ~('\"' | '\r' | '\n' | '\\') )* '\"' ; fragment Escape : '\\' ( 'a' | 'b' | 'f' | 'n' | 'r' | 't' | 'v' | '\"' | '\'' | '\\' | '?' | ('0'..'3') ( : Digit (Digit)? )? | ('4'..'7') (Digit)? | 'x' (Digit | 'a'..'f' | 'A'..'F')+ ) ; /* Numeric Constants: */ fragment Digit : '0'..'9' ; fragment Decimal : ('0'..'9')+ ; fragment LongSuffix : 'l' | 'L' ; fragment UnsignedSuffix : 'u' | 'U' ; fragment FloatSuffix : 'f' | 'F' ; fragment Exponent : ('e' | 'E') ('+' | '-')? (Digit)+ ; // fragment // Vocabulary // : '\3'..'\377' // ; // a numeric literal NUM @init {Token t=null;} : '.' {$type = DOT;} ( '.' '.' {$type = ELLIPSIS;} | ( ('0'..'9')+ (EXPONENT)? (f1=FLOAT_SUFFIX {t=f1;})? { if (t != null && t.getText().toUpperCase().indexOf('F')>=0) { $type = FLOAT; } else { $type = DOUBLE; // assume double } } )? ) | ( '0' {$type = DECIMALINT;} // special case for just '0' ( ('x'|'X') ( // hex // the 'e'|'E' and float suffix stuff look // like hex digits, hence the (...)+ doesn't // know when to stop: ambig. ANTLR resolves // it correctly by matching immediately. It // is therefor ok to hush warning. : HEX_DIGIT )+ {$type = HEXADECIMALINT;} | //float or double with leading zero (('0'..'9')+ ('.'|EXPONENT|FLOAT_SUFFIX)) => ('0'..'9')+ | ('0'..'7')+ {$type = OCTALINT;} )? | ('1'..'9') ('0'..'9')* {$type = DECIMALINT;} ) ( ('l'|'L') { $type = DECIMALINT; } // only check to see if it's a float if looks like decimal so far | {$type == DECIMALINT}? ( '.' ('0'..'9')* (EXPONENT)? (f2=FLOAT_SUFFIX {t=f2;})? | EXPONENT (f3=FLOAT_SUFFIX {t=f3;})? | f4=FLOAT_SUFFIX {t=f4;} ) { if (t != null && t.getText().toUpperCase() .indexOf('F') >= 0) { $type = FLOAT; } else { $type = DOUBLE; // assume double } } )? ; fragment FLOAT : ; fragment DOUBLE : ; fragment OCTALINT : ; fragment DECIMALINT : ; fragment HEXADECIMALINT : ; // Fragment methods to assist in matching floating point numbers fragment HEX_DIGIT : ('0'..'9'|'A'..'F'|'a'..'f') ; fragment EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ; fragment FLOAT_SUFFIX : 'f'|'F'|'d'|'D' ;