package project.phase2.ll1parsergenerator.dfastuff;
import java.util.*;
/**
* A class used to represent a character class during parsing.
*
*/
public class CharacterClass
{
/**
* The name of this class.
* This will be null if unnamed.
*/
private String mName;
/**
* The values in this character class.
*/
private Set<Character> mValues;
//
// CTOR
//
/**
* Creates a new character class with the given values and name.
*
* @param name the name of the character class.
* @param vals the values of the character class.
*/
public CharacterClass(String name, Set<Character> vals)
{
mName = name;
mValues = vals;
}
//
// PUBLIC METHODS
//
/**
* Generates an NFA to accept any of the values in this class.
*
* @return an NFA for any of the values in this class.
*/
public NFA getNFA()
{
NFA ret = new NFA();
Integer start = ret.createState();
ret.setStartState(start);
Integer state = ret.createState();
for(char c : mValues)
{
String s = Character.toString(c);
ret.addTransition(new Transition<String, Integer>(s, start, new Integer[]{state}));
ret.setGoalState(state);
}
return ret;
}
/**
* Returns whether or not this character class could still be the result for the given string if more characters were added.
*
* @param str the testing string.
* @return true if the given string is a substring of the name starting at the beginning, false otherwise.
*/
public boolean matchesClassName(String str)
{
if(str == null && mName == null)
return true;
else if(str == null || mName == null)
return false;
return mName.startsWith(str);
}
/**
* Sets the name.
*
* @param name the new name.
*/
public void setName(String name)
{
mName = name;
}
/**
* Returns the name.
*
* @return the name.
*/
public String getName()
{
return mName;
}
/**
* Merges this character class with the given one and returns the result.
*
* @param cc the character class to merge with.
* @return the merged character class.
*/
public CharacterClass merge(CharacterClass cc)
{
Set<Character> newSet = new HashSet<Character>();
newSet.addAll(this.mValues);
newSet.addAll(cc.mValues);
String name = null;
if(this.mName != null)
{
name = "";
name += this.mName;
}
if(cc.mName != null)
{
name = (name == null)?(cc.mName):(name + "+" + cc.mName);
}
return new CharacterClass(name, newSet);
}
/**
* Returns a representation of the character class with the same form as the ones given for fromSet.
* Specifically, this will create a character class of the form "[abcdefg]" if those are the characters in the class.
*
* @return a string representation of the character class.
*/
public String getClassDescriptor()
{
String ret = "[";
for(Character c : mValues)
{
for(char escp : Parser.classEscape)
{
if(escp == c)
{
ret += "\\";
break;
}
}
ret += c.toString();
}
ret += "]";
return ret;
}
//
// CLASS METHODS
//
/**
* Creates a new character class for the given range of values in the form "a-z".
*
* @param range the range of values in the form "a-z".
* @return a new character class.
*/
public static CharacterClass fromRange(String range)
{
Set<Character> chars = new HashSet<Character>();
if(range.length() != 3)
return null;
char ch1 = range.charAt(0), ch2 = range.charAt(2);
if((int)ch1 > (int)ch2 || range.charAt(1) != '-')
throw new IndexOutOfBoundsException("Invalid Range");
int start = (int)ch1, end = (int)ch2;
for(int i = start; i <= end; i++)
chars.add((char)i);
return new CharacterClass(null, chars);
}
/**
* Creates a new character class for the given set of values in the form "abcde".
* This character class will accept any one of the given values.
*
* @param set a string containing all possible values in the form "abcde".
* @return a new character class for the given set of values.
*/
public static CharacterClass fromSet(String set)
{
Set<Character> chars = new HashSet<Character>();
for(int i = 0; i < set.length(); i++)
chars.add(set.charAt(i));
return new CharacterClass(null, chars);
}
/**
* Creates a new character class from another character class excluding a set of values.
* @param cSmall the set of characters to exclude from cLarge.
* @param cLarge the set to exclude the characters from.
* @return a new character class.
*/
public static CharacterClass fromExclude(CharacterClass cSmall, CharacterClass cLarge)
{
Set<Character> finalSet = new HashSet<Character>();
finalSet.addAll(cLarge.mValues);
finalSet.removeAll(cSmall.mValues);
return new CharacterClass(null, finalSet);
}
public static void main(String[] args)
{
CharacterClass cc = CharacterClass.fromRange("a-h"), cc2;
cc.setName("A Through H");
System.out.println("Name Matching Tests");
System.out.println("Name: " + cc.getName());
System.out.println("'A Through H' Matches: " + cc.matchesClassName("A Through H"));
System.out.println("'Failure' Matches: " + cc.matchesClassName("Failure"));
System.out.println("'A T' Matches: " + cc.matchesClassName("A T"));
System.out.println("'A Through' Matches: " + cc.matchesClassName("A Through"));
System.out.println("'A Through H++' Matches: " + cc.matchesClassName("A Through H++"));
System.out.println();
System.out.println("NFA For 'a-h'");
System.out.println(cc.getNFA());
System.out.println();
cc2 = CharacterClass.fromSet("abcd");
System.out.println("NFA For 'abcd'");
System.out.println(cc2.getNFA());
System.out.println();
cc2 = CharacterClass.fromExclude(CharacterClass.fromRange("c-e"), cc);
System.out.println("NFA For exclude 'c-e' from 'a-h'");
System.out.println(cc2.getNFA());
System.out.println();
cc2 = CharacterClass.fromExclude(CharacterClass.fromSet("bdfh"), cc);
System.out.println("NFA For exclude 'bdfh' from 'a-h'");
System.out.println(cc2.getNFA());
System.out.println();
}
}