/** * Copyright 2012 Tobias Gierke <tobias.gierke@code-sourcery.de> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.codesourcery.jasm16.parser; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import de.codesourcery.jasm16.ast.ConstantValueNode; import de.codesourcery.jasm16.ast.NumberNode; import de.codesourcery.jasm16.ast.OperatorNode; import de.codesourcery.jasm16.ast.RegisterReferenceNode; import de.codesourcery.jasm16.ast.TermNode; import de.codesourcery.jasm16.compiler.ICompilationContext; import de.codesourcery.jasm16.compiler.ISymbolTable; import de.codesourcery.jasm16.exceptions.ParseException; /** * Enumeration of all supported operators. * * <p>Note that the current expression parser only supports * left-associative infix operators ; the {@link #INCREMENT} * and {@link #DECREMENT} operators are internally handled * by {@link RegisterReferenceNode} so the normally expression parser * never sees those. * </p> * @author tobias.gierke@code-sourcery.de */ public enum Operator { INCREMENT("++",9,OperatorPosition.POSTFIX) { @Override public long calculate(Long n1, Long n2) { throw new UnsupportedOperationException("not possible"); } @Override public int getRequiredOperandCount() { return 1; } }, DECREMENT("--",9,OperatorPosition.PREFIX) { @Override public long calculate(Long n1, Long n2) { throw new UnsupportedOperationException("not possible"); } @Override public int getRequiredOperandCount() { return 1; } }, BITWISE_OR("|",1,OperatorPosition.INFIX ) { @Override public long calculate(Long n1,Long n2) { return n1 | n2 ; } }, BITWISE_XOR("^",2,OperatorPosition.INFIX ) { @Override public long calculate(Long n1,Long n2) { return n1 ^ n2 ; } }, BITWISE_AND("&",3,OperatorPosition.INFIX ) { @Override public long calculate(Long n1,Long n2) { return n1 & n2 ; } }, // == != EQUAL("==",4,OperatorPosition.INFIX) { @Override public long calculate(Long n1, Long n2) { return n1.longValue() == n2.longValue() ? 1 : 0; } @Override public boolean isComparisonOperator() { return true; } }, NOT_EQUAL("!=",4,OperatorPosition.INFIX) { @Override public long calculate(Long n1, Long n2) { return n1.longValue() != n2.longValue() ? 1 : 0; } @Override public boolean isComparisonOperator() { return true; } }, // < > <= >= GREATER_OR_EQUAL(">=",5,OperatorPosition.INFIX) { @Override public long calculate(Long n1, Long n2) { return n1 >= n2 ? 1 : 0; } @Override public boolean isComparisonOperator() { return true; } }, LESS_OR_EQUAL("<=",5,OperatorPosition.INFIX) { @Override public long calculate(Long n1, Long n2) { return n1 <= n2 ? 1 : 0; } @Override public boolean isComparisonOperator() { return true; } }, GREATER_THAN(">",5,OperatorPosition.INFIX) { @Override public long calculate(Long n1, Long n2) { return n1 > n2 ? 1 : 0; } @Override public boolean isComparisonOperator() { return true; } }, LESS_THAN("<",5,OperatorPosition.INFIX) { @Override public long calculate(Long n1, Long n2) { return n1 < n2 ? 1 : 0; } @Override public boolean isComparisonOperator() { return true; } }, LEFT_SHIFT("<<",6,OperatorPosition.INFIX) { @Override public long calculate(Long n1, Long n2) { return n1 << n2; } }, RIGHT_SHIFT(">>",6,OperatorPosition.INFIX) { @Override public long calculate(Long n1,Long n2) { return n1 >> n2; } }, PLUS("+",7,OperatorPosition.INFIX) { @Override public long calculate(Long n1,Long n2) { return n1+n2; } }, MINUS("-",7,OperatorPosition.INFIX) { @Override public long calculate(Long n1,Long n2) { return n1-n2; } }, MODULO("%",8,OperatorPosition.INFIX) { @Override public long calculate(Long n1,Long n2) { return n1 % n2; } }, TIMES("*",8,OperatorPosition.INFIX) { @Override public long calculate(Long n1,Long n2) { return n1*n2; } }, DIVIDE("/",8,OperatorPosition.INFIX) { @Override public long calculate(Long n1,Long n2) { return n1 / n2; } }, BITWISE_NOT("~",9,OperatorPosition.PREFIX ) { @Override public long calculate(Long n1,Long n2) { return ~n1; } }, PARENS("(",100,OperatorPosition.PREFIX) { @Override protected long calculate(Long n1,Long n2) { throw new UnsupportedOperationException("Invoked on parens?"); } @Override public int getRequiredOperandCount() { return 1; } }; /** * Operator precedence. * Operators with higher precedence get evaluated earlier. */ private final int precedence; private final OperatorPosition[] positions; private final String literal; /** * Enumeration of possible operator positions (prefix,infix,postfix). * @author tobias.gierke@code-sourcery.de */ public enum OperatorPosition { PREFIX, INFIX, POSTFIX; } protected static final class Cache { protected static final Map<String,Operator> OPERATORS_BY_LITERAL = new HashMap<String,Operator>(); static { for ( Operator op : Operator.values() ) { OPERATORS_BY_LITERAL.put( op.literal , op ); } } public static Operator fromString(String s) { final Operator result = OPERATORS_BY_LITERAL.get( s ); if ( result == null ) { throw new IllegalArgumentException("Unknown operator '"+s+"'"); } return result; } public static boolean isValidOperator(String s) { return OPERATORS_BY_LITERAL.containsKey( s ); } } /** * Applies this operator to constant values. * * <p>This method is used by {@link TermNode#reduce(ICompilationContext)} * to calculate the literal value of an expression.</p> * * @param context * @param n1 * @param n2 * @return * @throws ParseException * @throws UnsupportedOperationException if this operator cannot be used for calculating * literal values */ protected long calculate(ISymbolTable table,ConstantValueNode n1, ConstantValueNode n2) throws ParseException,UnsupportedOperationException { return calculate( n1.getNumericValue( table ) , n2.getNumericValue( table ) ); } protected abstract long calculate(Long n1, Long n2) throws UnsupportedOperationException; public Long calculate(ISymbolTable table, OperatorNode node) { final Long value1 = node.getTerm( 0 ).calculate( table ); final TermNode term2 = node.getTerm(1); final Long value2 = term2 != null ? term2.calculate( table ) : null; // prefix operators receive their argument // as first value only if ( node.getOperator().isPrefixOperator() ) { if ( value1 != null ) { return calculate( value1 , null ); } } if ( value1 != null && value2 != null ) { return calculate( value1 , value2 ); } return null; } /** * Try to fold an {@link OperatorNode} into the corresponding * literal value. * * @param context * @param node * @return value of this expression after folding, may still resemble the * input value if reducing the expression to a literal value was not successful. * @see TermNode#reduce(ICompilationContext) */ public TermNode fold(ICompilationContext context , OperatorNode node) { final TermNode term1 = node.getTerm( 0 ).reduce( context ); final TermNode term2 = node.getTerm( 1 ).reduce( context ); if ( term1 instanceof ConstantValueNode && term2 instanceof ConstantValueNode) { long calculated; try { calculated = calculate( context.getSymbolTable() , (ConstantValueNode) term1, (ConstantValueNode) term2 ); } catch (ParseException e) { throw new RuntimeException("Should not happen...",e); } return new NumberNode( calculated , node.getTextRegion() ); } return new OperatorNode( node.getOperator() , term1 , term2 , node.getTextRegion() ); } private Operator(String literal,int precedence, OperatorPosition... positions) { this.literal = literal; this.positions = positions; this.precedence = precedence; } public boolean isComparisonOperator() { return false; } /** * Returns the number of operands supported by this operator. * * @return */ public int getRequiredOperandCount() { if ( isInfixOperator() ) { return 2; } return 1; } /** * Returns whether this is an prefix operator. * @return */ public boolean isPrefixOperator() { return supportsPosition( OperatorPosition.PREFIX ); } /** * Returns whether this is an infix operator. * @return */ public boolean isInfixOperator() { return supportsPosition( OperatorPosition.INFIX ); } /** * Returns whether this is a postfix opperator. * * @return */ public boolean isPostfixOperator() { return supportsPosition( OperatorPosition.POSTFIX ); } private boolean supportsPosition(OperatorPosition pos) { if (pos == null) { throw new IllegalArgumentException("position must not be NULL"); } for ( OperatorPosition actual : getSupportedPositions() ) { if ( actual == pos ) { return true; } } return false; } /** * Returns the positions this operator may occur at. * * @return */ public OperatorPosition[] getSupportedPositions() { return positions; } /** * Returns the string literal for this operator. * @return */ public String getLiteral() { return literal; } /** * Returns the operator instance for a given string literal. * * @param s * @return * @throws IllegalArgumentException if the input does not resemble a valid operator literal * @see #isValidOperator(String) */ public static Operator fromString(String s) throws IllegalArgumentException { final Operator result = Cache.OPERATORS_BY_LITERAL.get( s ); if ( result == null ) { throw new IllegalArgumentException("Unknown operator '"+s+"'"); } return result; } /** * Check whether a string resembles a known operator. * * @param s * @return */ public static boolean isValidOperator(String s) { return Cache.OPERATORS_BY_LITERAL.containsKey( s ); } /** * Check whether a given string is the prefix of * at least one operator. * * <p>Note that some operators are ambiguous , the one * with the longest prefix needs to be matched.</p> * * @param prefix * @return */ public static boolean isOperatorPrefix(String prefix) { for ( Operator op : values() ) { if ( op.literal.startsWith( prefix ) ) { return true; } } return false; } /** * Check whether a given character is the prefix of * at least one operator. * * <p>Note that some operators are ambiguous , the one * with the longest prefix needs to be matched.</p> * * @param prefix * @return */ public static boolean isOperatorPrefix(char prefix) { for ( Operator op : values() ) { if ( op.literal.charAt(0) == prefix ) { return true; } } return false; } /** * Returns all operators that match a given prefix. * * @param prefix * @return */ public static List<Operator> getPossibleOperatorsByPrefix(char prefix) { final List<Operator> result = new ArrayList<Operator>(); for ( Operator op : values() ) { if ( op.literal.charAt(0) == prefix ) { result.add( op ); } } return result; } /** * Picks the operator with the longest prefix match for a given input string. * * @param prefix * @return * @throws IllegalArgumentException If no operator could be found for the input prefix */ public static Operator pickOperatorWithLongestMatch(String prefix) { List<Operator> candidates = getPossibleOperatorsByPrefix( prefix ); if ( candidates.size() == 1 ) { return candidates.get(0); } else if ( candidates.isEmpty() ) { throw new IllegalArgumentException("Found no operators with prefix '"+prefix+"'"); } // look for perfect match first for ( Operator op : candidates ) { if ( op.literal.equals( prefix ) ) { return op; } } Operator result = null; int matchLength=0; for ( Operator op : candidates ) { int len = 0 ; for ( int i = 0 ; i < prefix.length() && i < op.literal.length() ; i++ ) { if ( op.literal.charAt( i ) == prefix.charAt( i ) ) { len++; } } if ( result == null || len > matchLength ) { matchLength = len; result = op; } } return result; } /** * Returns all operators for which a given input string is a prefix. * @param prefix * @return */ public static List<Operator> getPossibleOperatorsByPrefix(String prefix) { final List<Operator> result = new ArrayList<Operator>(); for ( Operator op : values() ) { if ( op.literal.startsWith( prefix ) ) { result.add( op ); } } return result; } /** * Returns the precedence of this operator. * * @return higher values mean higher precedence */ public int getPrecedence() { return precedence; } }