Add regex/2,3 predicate

regex/3 predicate takes a pattern term in the form of a regular
expression and either a single term or a list of terms to match the
pattern against. Returns each match.

regex/2 predicate takes the pattern and term(s) to match and returns
true if any match exists.

Change-Id: I20e18a84ea22f9c508aac9beb3136798b9c88eee
diff --git a/src/builtin/PRED_regex_compile_2.java b/src/builtin/PRED_regex_compile_2.java
new file mode 100644
index 0000000..142234f
--- /dev/null
+++ b/src/builtin/PRED_regex_compile_2.java
@@ -0,0 +1,47 @@
+package com.googlecode.prolog_cafe.builtin;
+import com.googlecode.prolog_cafe.lang.IllegalTypeException;
+import com.googlecode.prolog_cafe.lang.JavaObjectTerm;
+import com.googlecode.prolog_cafe.lang.Operation;
+import com.googlecode.prolog_cafe.lang.PInstantiationException;
+import com.googlecode.prolog_cafe.lang.Predicate;
+import com.googlecode.prolog_cafe.lang.Prolog;
+import com.googlecode.prolog_cafe.lang.SymbolTerm;
+import com.googlecode.prolog_cafe.lang.Term;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * <code>regex_compile/2</code><br>
+ *
+ * <pre>
+ *   'regex_compile'(+regex string, -Pattern object)
+ * </pre>
+ */
+public class PRED_regex_compile_2 extends Predicate.P2 {
+
+  public PRED_regex_compile_2(Term a1, Term a2, Operation cont) {
+        arg1 = a1;
+        arg2 = a2;
+        this.cont = cont;
+  }
+
+  public Operation exec(Prolog engine) {
+      engine.setB0();
+      Term a1 = arg1.dereference();
+      Term a2 = arg2.dereference();
+
+      if (a1.isVariable()) {
+        throw new PInstantiationException(this, 1);
+      }
+      if (!a1.isSymbol()) {
+        throw new IllegalTypeException(this, 1, "atom", a1);
+      }
+      Pattern pattern = Pattern.compile(a1.name());
+
+      if (!a2.unify(new JavaObjectTerm(pattern), engine.trail)) {
+        return engine.fail();
+      }
+      return cont;
+  }
+}
\ No newline at end of file
diff --git a/src/builtin/PRED_regex_match_3.java b/src/builtin/PRED_regex_match_3.java
new file mode 100644
index 0000000..830d787
--- /dev/null
+++ b/src/builtin/PRED_regex_match_3.java
@@ -0,0 +1,108 @@
+package com.googlecode.prolog_cafe.builtin;
+import com.googlecode.prolog_cafe.lang.IllegalTypeException;
+import com.googlecode.prolog_cafe.lang.JavaObjectTerm;
+import com.googlecode.prolog_cafe.lang.ListTerm;
+import com.googlecode.prolog_cafe.lang.Operation;
+import com.googlecode.prolog_cafe.lang.PInstantiationException;
+import com.googlecode.prolog_cafe.lang.Predicate;
+import com.googlecode.prolog_cafe.lang.Prolog;
+import com.googlecode.prolog_cafe.lang.SymbolTerm;
+import com.googlecode.prolog_cafe.lang.Term;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * <code>regex_match/3</code><br>
+ *
+ * <pre>
+ *   'regex_match'(+Pattern object, +Chars, -Matches)
+ * </pre>
+ */
+public class PRED_regex_match_3 extends Predicate.P3 {
+
+  private static final Operation regex_check = new PRED_regex_check();
+  private static final Operation regex_next = new PRED_regex_next();
+  private static final Operation regex_empty = new PRED_regex_empty();
+
+  public PRED_regex_match_3(Term a1, Term a2, Term a3, Operation cont) {
+        arg1 = a1;
+        arg2 = a2;
+        arg3 = a3;
+        this.cont = cont;
+  }
+
+  public Operation exec(Prolog engine) {
+      engine.setB0();
+      engine.cont = cont;
+      Term a1 = arg1.dereference();
+      Term a2 = arg2.dereference();
+
+      if (a1.isVariable()) {
+        throw new PInstantiationException(this, 1);
+      }
+      Pattern pattern = (Pattern)((JavaObjectTerm)a1).object();
+
+      if (a2.isVariable()) {
+        throw new PInstantiationException(this, 1);
+      }
+      if (!a2.isSymbol()) {
+        throw new IllegalTypeException(this, 1, "atom", a2);
+      }
+      Matcher matcher = pattern.matcher(a2.name());
+
+      if (!matcher.find()) {
+        return engine.fail();
+      }
+
+      engine.areg1 = new JavaObjectTerm(matcher);
+      engine.areg2 = arg3;
+      return engine.jtry2(regex_check, regex_next);
+  }
+
+  private static final class PRED_regex_check extends Operation {
+    @Override
+    public Operation exec(Prolog engine) {
+      Term a1 = engine.areg1;
+      Term result = engine.areg2;
+      Matcher matcher = (Matcher)((JavaObjectTerm)a1).object();
+
+      Term matches = getMatches(matcher);
+
+      if (matches == Prolog.Nil || !result.unify(matches, engine.trail)) {
+        return engine.fail();
+      }
+      return engine.cont;
+    }
+  }
+
+  private static final class PRED_regex_next extends Operation {
+    @Override
+    public Operation exec(Prolog engine) {
+      return engine.trust(regex_empty);
+    }
+  }
+
+  private static final class PRED_regex_empty extends Operation {
+    @Override
+    public Operation exec(Prolog engine) {
+      Term a1 = engine.areg1;
+      Matcher matcher = (Matcher)((JavaObjectTerm)a1).object();
+      if (!matcher.find()) {
+        return engine.fail();
+      }
+
+      return engine.jtry2(regex_check, regex_next);
+    }
+  }
+
+  private static Term getMatches(Matcher matcher) {
+    Term list = Prolog.Nil;
+    for (int i = matcher.groupCount(); i >= 0; i--) {
+      SymbolTerm match = SymbolTerm.create(matcher.group(i));
+      list = new ListTerm(match, list);
+    }
+    return list;
+  }
+}
\ No newline at end of file
diff --git a/src/builtin/builtins.pl b/src/builtin/builtins.pl
index f0e3178..b6dcda6 100644
--- a/src/builtin/builtins.pl
+++ b/src/builtin/builtins.pl
@@ -1693,6 +1693,8 @@
 %:- public char_code/2.                    written in Java
 %:- public number_chars/2, number_codes/2. written in Java
 :- public name/2.
+:- public regex/3.
+:- public regex/2.
 
 sub_atom(Atom, Before, Length, After, Sub_atom) :-
     atom_concat(AtomL, X, Atom),
@@ -1714,6 +1716,22 @@
 	;   illarg(type(list(char)), name(Constant,Chars), 2)
 	).
 
+regex(_, [], _) :- !, fail.
+regex(Pattern, List, Result) :-
+	List = [_ | _],
+	!,
+	regex_list(Pattern, List, Result).
+regex(Pattern, String, Result) :-
+	atom(String),
+	regex_compile(Pattern, Matcher),
+	regex_match(Matcher, String, Result).
+
+regex(Pattern, String) :-
+	once(regex(Pattern, String, _)).
+
+regex_list(Pattern, [H | _ ], Result) :- regex(Pattern, H, Result).
+regex_list(Pattern, [_ | Ls], Result) :- regex_list(Pattern, Ls, Result).
+
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 % Implementation defined hooks
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%