/** * 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.utils; import java.util.regex.Matcher; import java.util.regex.Pattern; import de.codesourcery.jasm16.exceptions.ParseException; /** * Provides helper methods for dealing with number literals in * hexadecimal/binary/decimal notation * * @author tobias.gierke@code-sourcery.de */ public class NumberLiteralHelper { private static final Pattern HEX_NUMBER=Pattern.compile("^0x([0-9a-fA-F]+)$"); private final static char[] HEX_CHARS = "0123456789abcdef".toCharArray(); private static final Pattern BIN_NUMBER=Pattern.compile("^b([0-1]+)$"); private static final Pattern DEC_NUMBER=Pattern.compile("^([\\-]{0,1}[0-9]+)$"); /** * Check whether a string resembles a valid number literal. * * @param s * @return */ public static boolean isNumberLiteral(String s) { return isHexLiteral( s ) || isBinaryLiteral( s ) || isDecimalLiteral( s ); } private static boolean isHexLiteral( String s ) { return HEX_NUMBER.matcher( s ).matches(); } private static boolean isBinaryLiteral(String s ) { return BIN_NUMBER.matcher( s ).matches(); } private static boolean isDecimalLiteral(String s ) { return DEC_NUMBER.matcher( s ).matches(); } private static long parseHex(String input) throws ParseException { outer: for ( int index = 0 ; index < input.length(); index++ ) { final char currentChar = input.charAt(index); for ( int i = 0 ; i < HEX_CHARS.length ; i++ ) { if ( HEX_CHARS[i] == currentChar ) { continue outer; } } throw new ParseException("Not a valid hex literal '"+currentChar+"'",index,1); } if ( input.length() > 8*2 ) { throw new ParseException("Hex literal to long (max. 8 bytes)", 0, input.length() ); } long result = 0; for ( int index = 0 ; index < input.length() ; index++) { final char currentChar = Character.toLowerCase( input.charAt(index) ); int i = 15; for ( ; i >= 0 ; i-- ) { if ( HEX_CHARS[i] == currentChar ) { break; } } result = result << 4; result = result | i; } return result; } /** * Returns the actual value of this number literal as a <code>long</code>. * * @return * @throws ParseException */ public static long parseValue(String s) throws ParseException { if ( isHexLiteral( s ) ) { final Matcher matcher = HEX_NUMBER.matcher( s ); matcher.matches(); final String rawContents = matcher.group( 1 ).toLowerCase(); return parseHex( rawContents ); } else if ( isBinaryLiteral( s ) ) { final Matcher matcher = BIN_NUMBER.matcher( s ); matcher.matches(); final String rawContents = matcher.group( 1 ).toLowerCase(); long result = 0; for ( char digit : rawContents.toCharArray() ) { result = result << 1; if ( digit == '1' ) { result = result | 1; } } return result; } else if ( isDecimalLiteral( s ) ) { Matcher matcher = DEC_NUMBER.matcher( s ); matcher.matches(); final String rawContents = matcher.group( 1 ).toLowerCase(); int index = 0; for ( ; index < rawContents.length() ; index++ ) { final char c = rawContents.charAt(index); if ( c != '-' && ! Character.isDigit( c ) ) { throw new ParseException("Not a digit: '"+c+"'",index,1); } } try { return Long.parseLong(rawContents); } catch(NumberFormatException e) { throw new ParseException("Number literal is too large: "+rawContents,0,rawContents.length()); } } throw new RuntimeException("Internal error, unhandled number literal: '"+s+"' ??"); } }