/*
* See LICENSE file in distribution for copyright and licensing information.
*/
package ioke.lang;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import org.jregex.Matcher;
import org.jregex.Pattern;
import org.jregex.MatchIterator;
import ioke.lang.exceptions.ControlFlow;
/**
* @author <a href="mailto:ola.bini@gmail.com">Ola Bini</a>
*/
public class Regexp extends IokeData {
private String pattern;
private Pattern regexp;
private String flags;
private Regexp(String pattern, Pattern regexp, String flags) {
this.pattern = pattern;
this.regexp = regexp;
this.flags = flags;
}
public static Regexp create(String pattern, String flags, IokeObject context, IokeObject message) throws ControlFlow {
try {
return new Regexp(pattern, new Pattern(pattern, flags), flags);
} catch(Exception e) {
e.printStackTrace();
return null;
}
}
static Regexp create(String pattern, String flags) {
try {
return new Regexp(pattern, new Pattern(pattern, flags), flags);
} catch(Exception e) {
e.printStackTrace();
return null;
}
}
public static String getPattern(Object on) throws ControlFlow {
return ((Regexp)IokeObject.data(on)).pattern;
}
public static Pattern getRegexp(Object on) throws ControlFlow {
return ((Regexp)IokeObject.data(on)).regexp;
}
public static String getFlags(Object on) throws ControlFlow {
return ((Regexp)IokeObject.data(on)).flags;
}
@Override
public void init(IokeObject obj) throws ControlFlow {
final Runtime runtime = obj.runtime;
obj.setKind("Regexp");
final IokeObject regexpMatch = new IokeObject(runtime, "contains behavior related to assignment", new RegexpMatch(obj, null, null));
regexpMatch.singleMimicsWithoutCheck(runtime.origin);
regexpMatch.init();
obj.registerCell("Match", regexpMatch);
obj.registerMethod(runtime.newNativeMethod("returns a hash for the regular expression", new NativeMethod.WithNoArguments("hash") {
@Override
public Object activate(IokeObject method, IokeObject context, IokeObject message, Object on) throws ControlFlow {
getArguments().getEvaluatedArguments(context, message, on, new ArrayList<Object>(), new HashMap<String, Object>());
Regexp r = (Regexp)IokeObject.data(on);
return context.runtime.newNumber(r.pattern.hashCode() + 13 * r.flags.hashCode());
}
}));
obj.registerMethod(runtime.newNativeMethod("returns true if the left hand side pattern is equal to the right hand side pattern.", new TypeCheckingNativeMethod("==") {
private final TypeCheckingArgumentsDefinition ARGUMENTS = TypeCheckingArgumentsDefinition
.builder()
.receiverMustMimic(runtime.regexp)
.withRequiredPositional("other")
.getArguments();
@Override
public TypeCheckingArgumentsDefinition getArguments() {
return ARGUMENTS;
}
@Override
public Object activate(IokeObject self, Object on, List<Object> args, Map<String, Object> keywords, IokeObject context, IokeObject message) throws ControlFlow {
getArguments().getEvaluatedArguments(context, message, on, args, new HashMap<String, Object>());
Object other = args.get(0);
return (((other instanceof IokeObject) &&
(IokeObject.data(other) instanceof Regexp) &&
((on == context.runtime.regexp || other == context.runtime.regexp) ? on == other :
(((Regexp)IokeObject.data(on)).pattern.equals(((Regexp)IokeObject.data(other)).pattern) &&
((Regexp)IokeObject.data(on)).flags.equals(((Regexp)IokeObject.data(other)).flags))))) ?
context.runtime._true : context.runtime._false;
}
}));
obj.registerMethod(runtime.newNativeMethod("Returns the pattern use for this regular expression", new TypeCheckingNativeMethod.WithNoArguments("pattern", runtime.regexp) {
@Override
public Object activate(IokeObject method, Object on, List<Object> args, Map<String, Object> keywords, IokeObject context, IokeObject message) throws ControlFlow {
return context.runtime.newText(getPattern(on));
}
}));
obj.registerMethod(runtime.newNativeMethod("Takes one argument and tries to match that argument against the current pattern. Returns nil if no match can be done, or a Regexp Match object if a match succeeds", new TypeCheckingNativeMethod("match") {
private final TypeCheckingArgumentsDefinition ARGUMENTS = TypeCheckingArgumentsDefinition
.builder()
.receiverMustMimic(runtime.regexp)
.withRequiredPositional("other")
.getArguments();
@Override
public TypeCheckingArgumentsDefinition getArguments() {
return ARGUMENTS;
}
@Override
public Object activate(IokeObject method, Object on, List<Object> args, Map<String, Object> keywords, IokeObject context, IokeObject message) throws ControlFlow {
IokeObject target = IokeObject.as(Interpreter.send(context.runtime.asText, context, args.get(0)), context);
String arg = Text.getText(target);
Matcher m = ((Regexp)IokeObject.data(on)).regexp.matcher(arg);
if(m.find()) {
IokeObject match = regexpMatch.allocateCopy(message, context);
match.singleMimicsWithoutCheck(regexpMatch);
match.setData(new RegexpMatch(IokeObject.as(on, context), m, target));
return match;
} else {
return context.runtime.nil;
}
}
}));
obj.aliasMethod("match", "=~", null, null);
obj.registerMethod(runtime.newNativeMethod("Takes one argument that should be a text and returns a text that has all regexp meta characters quoted", new NativeMethod("quote") {
private final DefaultArgumentsDefinition ARGUMENTS = DefaultArgumentsDefinition
.builder()
.withRequiredPositional("text")
.getArguments();
@Override
public DefaultArgumentsDefinition getArguments() {
return ARGUMENTS;
}
@Override
public Object activate(IokeObject method, Object on, List<Object> args, Map<String, Object> keywords, IokeObject context, IokeObject message) throws ControlFlow {
return context.runtime.newText(Pattern.quote(Text.getText(Interpreter.send(context.runtime.asText, context, args.get(0)))));
}
}));
obj.registerMethod(runtime.newNativeMethod("Takes one or two text arguments that describes the regular expression to create. the first text is the pattern and the second is the flags.", new NativeMethod("from") {
private final DefaultArgumentsDefinition ARGUMENTS = DefaultArgumentsDefinition
.builder()
.withRequiredPositional("pattern")
.withOptionalPositional("flags", "")
.getArguments();
@Override
public DefaultArgumentsDefinition getArguments() {
return ARGUMENTS;
}
@Override
public Object activate(IokeObject method, Object on, List<Object> args, Map<String, Object> keywords, IokeObject context, IokeObject message) throws ControlFlow {
String pattern = Text.getText(Interpreter.send(context.runtime.asText, context, args.get(0)));
String flags = "";
if(args.size() > 1) {
flags = Text.getText(Interpreter.send(context.runtime.asText, context, args.get(1)));
}
return context.runtime.newRegexp(pattern, flags, context, message);
}
}));
obj.registerMethod(runtime.newNativeMethod("Takes one argument and tries to match that argument against the current pattern. Returns a list of all the texts that were matched.", new TypeCheckingNativeMethod("allMatches") {
private final TypeCheckingArgumentsDefinition ARGUMENTS = TypeCheckingArgumentsDefinition
.builder()
.receiverMustMimic(runtime.regexp)
.withRequiredPositional("other")
.getArguments();
@Override
public TypeCheckingArgumentsDefinition getArguments() {
return ARGUMENTS;
}
@Override
public Object activate(IokeObject method, Object on, List<Object> args, Map<String, Object> keywords, IokeObject context, IokeObject message) throws ControlFlow {
String arg = Text.getText(Interpreter.send(context.runtime.asText, context, args.get(0)));
Matcher m = ((Regexp)IokeObject.data(on)).regexp.matcher(arg);
List<Object> result = new ArrayList<Object>();
MatchIterator iter = m.findAll();
Runtime runtime = context.runtime;
while(iter.hasMore()) {
result.add(runtime.newText(iter.nextMatch().group(0)));
}
return runtime.newList(result);
}
}));
obj.registerMethod(runtime.newNativeMethod("Returns a text inspection of the object", new TypeCheckingNativeMethod.WithNoArguments("inspect", runtime.regexp) {
@Override
public Object activate(IokeObject method, Object on, List<Object> args, Map<String, Object> keywords, IokeObject context, IokeObject message) throws ControlFlow {
return method.runtime.newText(Regexp.getInspect(on));
}
}));
obj.registerMethod(runtime.newNativeMethod("Returns a brief text inspection of the object", new TypeCheckingNativeMethod.WithNoArguments("notice", runtime.regexp) {
@Override
public Object activate(IokeObject method, Object on, List<Object> args, Map<String, Object> keywords, IokeObject context, IokeObject message) throws ControlFlow {
return method.runtime.newText(Regexp.getNotice(on));
}
}));
obj.registerMethod(runtime.newNativeMethod("returns a list of all the named groups in this regular expression", new TypeCheckingNativeMethod.WithNoArguments("names", runtime.regexp) {
@Override
public Object activate(IokeObject method, Object on, List<Object> args, Map<String, Object> keywords, IokeObject context, IokeObject message) throws ControlFlow {
Set names = Regexp.getRegexp(on).getGroupNames();
List<Object> theNames = new ArrayList<Object>();
for(Object name : names) {
theNames.add(context.runtime.getSymbol(((String)name)));
}
return context.runtime.newList(theNames);
}
}));
}
public static String getInspect(Object on) throws ControlFlow {
return ((Regexp)(IokeObject.data(on))).inspect(on);
}
public static String getNotice(Object on) throws ControlFlow {
return ((Regexp)(IokeObject.data(on))).notice(on);
}
public String inspect(Object obj) throws ControlFlow {
return "#/" + pattern + "/" + flags;
}
public String notice(Object obj) throws ControlFlow {
return "#/" + pattern + "/" + flags;
}
}// Regexp