/**
* 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 de.codesourcery.jasm16.OpCode;
import de.codesourcery.jasm16.Register;
import de.codesourcery.jasm16.ast.LabelNode;
import de.codesourcery.jasm16.exceptions.ParseException;
import de.codesourcery.jasm16.utils.ITextRegion;
/**
* A valid , IMMUTABLE identifier in the assembler source code.
*
* <p>Currently this assembler only supports labels.</p>
*
* @author tobias.gierke@code-sourcery.de
* @see LabelNode
*/
public class Identifier
{
private static final char[] VALID_CHARACTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$".toCharArray();
private final String identifier; // client code relies on identifiers being immutable !!!
/**
* Create identifier.
*
* @param identifier
* @throws ParseException If the input string is not a valid identifier.
*/
public Identifier(String identifier) throws ParseException
{
if (identifier == null) {
throw new IllegalArgumentException("identifier must not be NULL.");
}
if ( identifier.length() == 0 ) {
throw new ParseException("Identifier expected", 0 ,0 );
}
final char[] actual = identifier.toCharArray();
int i = 0;
outer:
for ( ; i < actual.length ; i++ )
{
final char currentChar = actual[i];
for ( char validChar : VALID_CHARACTERS ) {
if ( currentChar == validChar ) {
continue outer;
}
}
throw new ParseException("Found invalid character '"+currentChar+"' in identifier '"+identifier+"'", i , 1);
}
this.identifier = identifier;
}
public static boolean isValidIdentifier(String identifier)
{
if (identifier == null) {
return false;
}
if ( identifier.length() == 0 ) {
return false;
}
final char[] actual = identifier.toCharArray();
int i = 0;
outer:
for ( ; i < actual.length ; i++ )
{
final char currentChar = actual[i];
for ( char validChar : VALID_CHARACTERS ) {
if ( currentChar == validChar ) {
continue outer;
}
}
if ( i == 0 && currentChar == '.' ) { // local labels may start with '.'
continue;
}
return false;
}
return true;
}
@Override
public boolean equals(Object obj)
{
if ( this == obj ) {
return true;
}
if ( obj instanceof Identifier) {
return this.identifier.equals( ((Identifier) obj).identifier );
}
return false;
}
@Override
public int hashCode() {
return identifier.hashCode();
}
/**
* Returns the 'raw' value (as returned by the lexer) of
* this identifier.
*
* @return
*/
public String getRawValue() {
return identifier;
}
@Override
public String toString()
{
return identifier;
}
/**
* Check whether a character may be used in an identifier.
*
* <p>This method is used by the lexer.</p>
*
* @param c
* @return
*/
public static boolean isValidIdentifierChar(char c)
{
for ( char valid : VALID_CHARACTERS ) {
if ( valid == c) {
return true;
}
}
return false;
}
/**
* Performs additional checks (not an opcode etc.) on an identifier , throwing a {@link ParseException}
* if any of these checks fails.
*
* @param s
* @param textRegion text region to report when throwing a <code>ParseException</code>
* @throws ParseException
*/
public static void assertValidIdentifier(String s,ITextRegion textRegion) throws ParseException
{
final OpCode op = OpCode.fromIdentifier( s );
if ( op != null ) {
throw new ParseException("Reserved keywords (opcode) must not be used as identifiers", textRegion );
}
final Register register = Register.fromString( s );
if ( register != null ) {
throw new ParseException("Reserved keywords (register name) must not be used as identifiers", textRegion );
}
}
}