package btools.expressions; import java.util.StringTokenizer; final class BExpression { private static final int OR_EXP = 10; private static final int AND_EXP = 11; private static final int NOT_EXP = 12; private static final int ADD_EXP = 20; private static final int MULTIPLY_EXP = 21; private static final int MAX_EXP = 22; private static final int EQUAL_EXP = 23; private static final int GREATER_EXP = 24; private static final int MIN_EXP = 25; private static final int SUB_EXP = 26; private static final int LESSER_EXP = 27; private static final int XOR_EXP = 28; private static final int SWITCH_EXP = 30; private static final int ASSIGN_EXP = 31; private static final int LOOKUP_EXP = 32; private static final int NUMBER_EXP = 33; private static final int VARIABLE_EXP = 34; private static final int FOREIGN_VARIABLE_EXP = 35; private int typ; private BExpression op1; private BExpression op2; private BExpression op3; private float numberValue; private int variableIdx; private int lookupNameIdx; private int[] lookupValueIdxArray; // Parse the expression and all subexpression public static BExpression parse( BExpressionContext ctx, int level ) throws Exception { return parse( ctx, level, null ); } private static BExpression parse( BExpressionContext ctx, int level, String optionalToken ) throws Exception { boolean brackets = false; String operator = ctx.parseToken(); if ( optionalToken != null && optionalToken.equals( operator ) ) { operator = ctx.parseToken(); } if ( "(".equals( operator ) ) { brackets = true; operator = ctx.parseToken(); } if ( operator == null ) { if ( level == 0 ) return null; else throw new IllegalArgumentException( "unexpected end of file" ); } if ( level == 0 ) { if ( !"assign".equals( operator ) ) { throw new IllegalArgumentException( "operator " + operator + " is invalid on toplevel (only 'assign' allowed)" ); } } BExpression exp = new BExpression(); int nops = 3; boolean ifThenElse = false; if ( "switch".equals( operator ) ) { exp.typ = SWITCH_EXP; } else if ( "if".equals( operator ) ) { exp.typ = SWITCH_EXP; ifThenElse = true; } else { nops = 2; // check binary expressions if ( "or".equals( operator ) ) { exp.typ = OR_EXP; } else if ( "and".equals( operator ) ) { exp.typ = AND_EXP; } else if ( "multiply".equals( operator ) ) { exp.typ = MULTIPLY_EXP; } else if ( "add".equals( operator ) ) { exp.typ = ADD_EXP; } else if ( "max".equals( operator ) ) { exp.typ = MAX_EXP; } else if ( "min".equals( operator ) ) { exp.typ = MIN_EXP; } else if ( "equal".equals( operator ) ) { exp.typ = EQUAL_EXP; } else if ( "greater".equals( operator ) ) { exp.typ = GREATER_EXP; } else if ( "sub".equals( operator ) ) { exp.typ = SUB_EXP; } else if ( "lesser".equals( operator ) ) { exp.typ = LESSER_EXP; } else if ( "xor".equals( operator ) ) { exp.typ = XOR_EXP; } else { nops = 1; // check unary expressions if ( "assign".equals( operator ) ) { if ( level > 0 ) throw new IllegalArgumentException( "assign operator within expression" ); exp.typ = ASSIGN_EXP; String variable = ctx.parseToken(); if ( variable == null ) throw new IllegalArgumentException( "unexpected end of file" ); if ( variable.indexOf( '=' ) >= 0 ) throw new IllegalArgumentException( "variable name cannot contain '=': " + variable ); if ( variable.indexOf( ':' ) >= 0 ) throw new IllegalArgumentException( "cannot assign context-prefixed variable: " + variable ); exp.variableIdx = ctx.getVariableIdx( variable, true ); if ( exp.variableIdx < ctx.getMinWriteIdx() ) throw new IllegalArgumentException( "cannot assign to readonly variable " + variable ); } else if ( "not".equals( operator ) ) { exp.typ = NOT_EXP; } else { nops = 0; // check elemantary expressions int idx = operator.indexOf( '=' ); if ( idx >= 0 ) { exp.typ = LOOKUP_EXP; String name = operator.substring( 0, idx ); String values = operator.substring( idx+1 ); exp.lookupNameIdx = ctx.getLookupNameIdx( name ); if ( exp.lookupNameIdx < 0 ) { throw new IllegalArgumentException( "unknown lookup name: " + name ); } ctx.markLookupIdxUsed( exp.lookupNameIdx ); StringTokenizer tk = new StringTokenizer( values, "|" ); int nt = tk.countTokens(); int nt2 = nt == 0 ? 1 : nt; exp.lookupValueIdxArray = new int[nt2]; for( int ti=0; ti= 0 ) { String context = operator.substring( 0, idx ); String varname = operator.substring( idx+1 ); exp.typ = FOREIGN_VARIABLE_EXP; exp.variableIdx = ctx.getForeignVariableIdx( context, varname ); } else if ( (idx = ctx.getVariableIdx( operator, false )) >= 0 ) { exp.typ = VARIABLE_EXP; exp.variableIdx = idx; } else if ( "true".equals( operator ) ) { exp.numberValue = 1.f; exp.typ = NUMBER_EXP; } else if ( "false".equals( operator ) ) { exp.numberValue = 0.f; exp.typ = NUMBER_EXP; } else { try { exp.numberValue = Float.parseFloat( operator ); exp.typ = NUMBER_EXP; } catch( NumberFormatException nfe ) { throw new IllegalArgumentException( "unknown expression: " + operator ); } } } } } // parse operands if ( nops > 0 ) { exp.op1 = BExpression.parse( ctx, level+1, exp.typ == ASSIGN_EXP ? "=" : null ); } if ( nops > 1 ) { if ( ifThenElse ) checkExpectedToken( ctx, "then" ); exp.op2 = BExpression.parse( ctx, level+1, null ); } if ( nops > 2 ) { if ( ifThenElse ) checkExpectedToken( ctx, "else" ); exp.op3 = BExpression.parse( ctx, level+1, null ); } if ( brackets ) { checkExpectedToken( ctx, ")" ); } return exp; } private static void checkExpectedToken( BExpressionContext ctx, String expected ) throws Exception { String token = ctx.parseToken(); if ( ! expected.equals( token ) ) { throw new IllegalArgumentException( "unexpected token: " + token + ", expected: " + expected ); } } // Evaluate the expression public float evaluate( BExpressionContext ctx ) { switch( typ ) { case OR_EXP: return op1.evaluate(ctx) != 0.f ? 1.f : ( op2.evaluate(ctx) != 0.f ? 1.f : 0.f ); case XOR_EXP: return ( (op1.evaluate(ctx) != 0.f) ^ ( op2.evaluate(ctx) != 0.f ) ? 1.f : 0.f ); case AND_EXP: return op1.evaluate(ctx) != 0.f ? ( op2.evaluate(ctx) != 0.f ? 1.f : 0.f ) : 0.f; case ADD_EXP: return op1.evaluate(ctx) + op2.evaluate(ctx); case SUB_EXP: return op1.evaluate(ctx) - op2.evaluate(ctx); case MULTIPLY_EXP: return op1.evaluate(ctx) * op2.evaluate(ctx); case MAX_EXP: return max( op1.evaluate(ctx), op2.evaluate(ctx) ); case MIN_EXP: return min( op1.evaluate(ctx), op2.evaluate(ctx) ); case EQUAL_EXP: return op1.evaluate(ctx) == op2.evaluate(ctx) ? 1.f : 0.f; case GREATER_EXP: return op1.evaluate(ctx) > op2.evaluate(ctx) ? 1.f : 0.f; case LESSER_EXP: return op1.evaluate(ctx) < op2.evaluate(ctx) ? 1.f : 0.f; case SWITCH_EXP: return op1.evaluate(ctx) != 0.f ? op2.evaluate(ctx) : op3.evaluate(ctx); case ASSIGN_EXP: return ctx.assign( variableIdx, op1.evaluate(ctx) ); case LOOKUP_EXP: return ctx.getLookupMatch( lookupNameIdx, lookupValueIdxArray ); case NUMBER_EXP: return numberValue; case VARIABLE_EXP: return ctx.getVariableValue( variableIdx ); case FOREIGN_VARIABLE_EXP: return ctx.getForeignVariableValue( variableIdx ); case NOT_EXP: return op1.evaluate(ctx) == 0.f ? 1.f : 0.f; default: throw new IllegalArgumentException( "unknown op-code: " + typ ); } } private float max( float v1, float v2 ) { return v1 > v2 ? v1 : v2; } private float min( float v1, float v2 ) { return v1 < v2 ? v1 : v2; } }