/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package builtin.matcher;
import builtin.BuiltinSub;
import gui.Constants;
import gui.MintException;
import gui.Pointer;
import gui.PointerTools;
import gui.SmartList;
/**
* @author Oliver Chu
*/
public class Matches extends BuiltinSub {
private static final int STAY_SAME = -128;
private static final int DECREMENT_THIS = -64;
private static final int DOES_NOT_MATCH = Integer.MIN_VALUE + 1;
private static final int NORMAL_MATCH = 1023;
private static final int MOVE_FORWARD_X_BASE = 1024;
public static final int ANY_CHARACTER = 0;
public static final Pointer ANY_CHAR_MATCH =
new Pointer(Constants.MATCHER_TYPE, ANY_CHARACTER);
public static final Pointer ANY_UPPER_OR_LOWERCASE_LETTER =
new Pointer(Constants.MATCHER_TYPE, 1);
public static final Pointer ANY_DECIMAL_DIGIT =
new Pointer(Constants.MATCHER_TYPE, 2);
public static final Pointer ANY_HEXADECIMAL_DIGIT =
new Pointer(Constants.MATCHER_TYPE, 3);
public static final Pointer ANY_BINARY_DIGIT =
new Pointer(Constants.MATCHER_TYPE, 4);
public static final Pointer ANY_UPPERCASE_LETTER =
new Pointer(Constants.MATCHER_TYPE, 5);
public static final Pointer ANY_OPEN_BRACE_OR_BRACKET =
new Pointer(Constants.MATCHER_TYPE, 6);
public static final Pointer ANY_SYMBOL =
new Pointer(Constants.MATCHER_TYPE, 7);
public static final Pointer ANY_CLOSE_BRACE_OR_BRACKET =
new Pointer(Constants.MATCHER_TYPE, 8);
public static final Pointer ANY_WHITESPACE =
new Pointer(Constants.MATCHER_TYPE, 9);
public static final Pointer ANY_REGEX_METACHARACTER =
new Pointer(Constants.MATCHER_TYPE, 10);
public static final int Z_OR_MORE_OF_NXT = 11;
public static final Pointer ZERO_OR_MORE_OF_NEXT =
new Pointer(Constants.MATCHER_TYPE, Z_OR_MORE_OF_NXT);
public int characterMatches(char c, SmartList<Pointer> needleList) {
if (needleList.isEmpty()) {
return DOES_NOT_MATCH;
}
Pointer functor = needleList.get(0);
switch (functor.type) {
case Constants.STR_TYPE: {
String function = PointerTools.dereferenceString(functor);
if (function.length() == 1) {
// If we are only checking 1 character,
// just check for that one only.
if (c == function.charAt(0)) {
return NORMAL_MATCH;
} else {
return DOES_NOT_MATCH;
}
} else {
// Otherwise,
// check if our character c
// is equal to the first, or the second, or the
// third... etc. characters of the given string.
for (char d : function.toCharArray()) {
if (c == d) {
return NORMAL_MATCH;
}
}
return DOES_NOT_MATCH;
}
} case Constants.MATCHER_TYPE: {
switch (functor.value) {
case ANY_CHARACTER:
// Anything works. We don't care what the character is.
return NORMAL_MATCH;
case Z_OR_MORE_OF_NXT:
String nextChars =
PointerTools.dereferenceString(needleList.get(1));
if (nextChars == null) {
// Sorry, but this meta list-value
// must be followed by a string, not another
// matcher.
return DOES_NOT_MATCH;
}
if (nextChars.contains("" + c)) {
// We didn't find all of them yet.
// The for loop will increment the
// index for the current character,
// but not for the needle list.
return STAY_SAME;
} else {
// We successfully found all of them.
// So now move forward past ourselves
// and past the character(s) we were checking.
return MOVE_FORWARD_X_BASE + 2;
}
default:
return DOES_NOT_MATCH;
}
} case Constants.INT_TYPE: {
int val = functor.value;
if (val < 1) {
// We are done, so move on.
// The reason we are done is because the
// needle list has told us to "check for 0 of something"
// which is always true.
return MOVE_FORWARD_X_BASE + 2;
} else {
String nextChars =
PointerTools.dereferenceString(needleList.get(1));
if (nextChars == null) {
// The next set of character(s)
// is not a Mint string!
// We can't do any kind of checking!
return DOES_NOT_MATCH;
} else {
// Found it. Next time, we have to check for 1
// less character.
if (nextChars.contains("" + c)) {
return DECREMENT_THIS;
} else {
// This isn't the same!
return DOES_NOT_MATCH;
}
}
}
} default:
// The only legal list-values are...
//
// Mint strings of length 1 or more,
// equivalent to the regex "[abcdef]" if you
// have the string "abcdef"
//
// Integers, in which [20, "a", 30, "b"]
// is the same as the regex "a{20}b{30}"
//
// Meta list-values, such as zeroOrMoreOfNext
// or anyCharacter.
// ["ft", zeroOrMoreOfNext, "t"] is the same as the regex
// "[ft]t*" or "ft+".
// ["bad", anyCharacter, anyCharacter, "apple"] is the
// same as the regex
// "bad.{2}apple" or "bad..apple"
//
// Since this particular case isn't legal, we return
// the value DOES_NOT_MATCH.
return DOES_NOT_MATCH;
}
}
/** Performs a Lisp (cdr someList) for 'number' number of times,
* and returns the result.
* if the 'number' is negative, performs a (cons MINT_NULL someList)
* 'number' number of times, and then returns that.
*/
public SmartList<Pointer> chomp(int number, SmartList<Pointer> someList) {
if (number == 0) {
return someList;
}
if (number < 0) {
int limit = -number;
SmartList<Pointer> nullDriver = new SmartList<Pointer>();
for (int i = 0; i < limit; ++i) {
nullDriver.add(Constants.MINT_NULL);
}
SmartList<Pointer> nList = new SmartList<Pointer>();
nList.addAll(nullDriver);
nList.addAll(someList);
return nList;
} else {
// We can't chomp an empty list.
if (someList.isEmpty()) {
return someList;
}
SmartList<Pointer> someOtherList = new SmartList<Pointer>();
for (int i = number; i < someList.size(); i++) {
someOtherList.add(someList.get(i));
}
return someOtherList;
}
}
@Override
public Pointer apply(SmartList<Pointer> args) throws MintException {
if (args.size() < 2) {
return Constants.MINT_FALSE;
}
String haystack = PointerTools.dereferenceString(args.get(0));
SmartList<Pointer> needleList =
PointerTools.dereferenceList(args.get(1));
if (haystack == null || needleList == null) {
return Constants.MINT_FALSE;
}
if (needleList.isEmpty()) {
return Constants.MINT_FALSE;
}
for (char c : haystack.toCharArray()) {
int matchCode = characterMatches(c, needleList);
if (matchCode == NORMAL_MATCH) {
needleList = chomp(1, needleList);
} else if (matchCode == DOES_NOT_MATCH) {
return Constants.MINT_FALSE;
} else {
if (matchCode > MOVE_FORWARD_X_BASE) {
needleList =
chomp(matchCode - MOVE_FORWARD_X_BASE, needleList);
} else if (matchCode == DECREMENT_THIS) {
needleList.set(0,
new Pointer(Constants.INT_TYPE,
needleList.get(0).value - 1));
// We chomp because we are done checking for this
// number of chars.
if (needleList.get(0).value == 0) {
needleList = chomp(1, needleList);
}
} // else if (STAY_SAME) { do nothing } else { do nothing }
}
}
return Constants.MINT_TRUE;
}
}