Добавлен модуль regex

This commit is contained in:
Victor 2018-12-13 20:08:02 +02:00
parent 8965174589
commit a30ca2fd4d
5 changed files with 317 additions and 0 deletions

View File

@ -0,0 +1,171 @@
package com.annimon.ownlang.modules.regex;
import com.annimon.ownlang.exceptions.TypeException;
import com.annimon.ownlang.lib.*;
import java.util.regex.Matcher;
public class MatcherValue extends MapValue {
private final Matcher value;
public MatcherValue(Matcher value) {
super(22);
this.value = value;
init();
}
private void init() {
set("start", this::start);
set("end", this::end);
set("find", this::find);
set("group", this::group);
set("pattern", this::pattern);
set("region", this::region);
set("replaceFirst", this::replaceFirst);
set("replaceAll", this::replaceAll);
set("replaceCallback", this::replaceCallback);
set("reset", this::reset);
set("usePattern", this::usePattern);
set("useAnchoringBounds", this::useAnchoringBounds);
set("hasAnchoringBounds", Converters.voidToBoolean(value::hasAnchoringBounds));
set("useTransparentBounds", this::useTransparentBounds);
set("hasTransparentBounds", Converters.voidToBoolean(value::hasTransparentBounds));
set("hitEnd", Converters.voidToBoolean(value::hitEnd));
set("lookingAt", Converters.voidToBoolean(value::lookingAt));
set("matches", Converters.voidToBoolean(value::matches));
set("groupCount", Converters.voidToInt(value::groupCount));
set("regionStart", Converters.voidToInt(value::regionStart));
set("regionEnd", Converters.voidToInt(value::regionEnd));
}
private Value start(Value[] args) {
Arguments.checkOrOr(0, 1, args.length);
final int result;
if (args.length == 0) {
result = value.start();
} else {
final Value arg = args[0];
if (arg.type() == Types.NUMBER) {
result = value.start(arg.asInt());
} else {
result = value.start(arg.asString());
}
}
return NumberValue.of(result);
}
private Value end(Value[] args) {
Arguments.checkOrOr(0, 1, args.length);
final int result;
if (args.length == 0) {
result = value.end();
} else {
final Value arg = args[0];
if (arg.type() == Types.NUMBER) {
result = value.end(arg.asInt());
} else {
result = value.end(arg.asString());
}
}
return NumberValue.of(result);
}
private Value find(Value[] args) {
Arguments.checkOrOr(0, 1, args.length);
final boolean result;
if (args.length == 0) {
result = value.find();
} else {
result = value.find(args[0].asInt());
}
return NumberValue.fromBoolean(result);
}
private Value group(Value[] args) {
Arguments.checkOrOr(0, 1, args.length);
final String result;
if (args.length == 0) {
result = value.group();
} else {
final Value arg = args[0];
if (arg.type() == Types.NUMBER) {
result = value.group(arg.asInt());
} else {
result = value.group(arg.asString());
}
}
return new StringValue(result);
}
private Value pattern(Value[] args) {
return new PatternValue(value.pattern());
}
private Value region(Value[] args) {
Arguments.check(2, args.length);
value.region(args[0].asInt(), args[1].asInt());
return this;
}
private Value replaceFirst(Value[] args) {
Arguments.check(1, args.length);
return new StringValue(value.replaceFirst(args[0].asString()));
}
private Value replaceAll(Value[] args) {
Arguments.check(1, args.length);
return new StringValue(value.replaceAll(args[0].asString()));
}
static StringValue replaceCallback(MatcherValue matcherValue, Function callback) {
final Matcher matcher = matcherValue.value;
final StringBuffer sb = new StringBuffer();
while (matcher.find()) {
String replacement = callback.execute(matcherValue).asString();
matcher.appendReplacement(sb, replacement);
}
matcher.appendTail(sb);
return new StringValue(sb.toString());
}
private Value replaceCallback(Value[] args) {
Arguments.check(1, args.length);
if (args[0].type() != Types.FUNCTION) {
throw new TypeException(args[0].toString() + " is not a function");
}
return replaceCallback(this, ((FunctionValue) args[0]).getValue());
}
private Value reset(Value[] args) {
Arguments.checkOrOr(0, 1, args.length);
if (args.length == 0) {
value.reset();
} else {
value.reset(args[0].asString());
}
return this;
}
private Value useAnchoringBounds(Value[] args) {
Arguments.check(1, args.length);
value.useAnchoringBounds(args[0].asInt() != 0);
return this;
}
private Value useTransparentBounds(Value[] args) {
Arguments.check(1, args.length);
value.useTransparentBounds(args[0].asInt() != 0);
return this;
}
private Value usePattern(Value[] args) {
Arguments.check(1, args.length);
final Value arg = args[0];
if (arg.type() == Types.MAP && (arg instanceof PatternValue)) {
value.usePattern(((PatternValue) arg).getValue());
} else {
throw new TypeException("Pattern value expected");
}
return this;
}
}

View File

@ -0,0 +1,62 @@
package com.annimon.ownlang.modules.regex;
import com.annimon.ownlang.exceptions.TypeException;
import com.annimon.ownlang.lib.Arguments;
import com.annimon.ownlang.lib.ArrayValue;
import com.annimon.ownlang.lib.Converters;
import com.annimon.ownlang.lib.FunctionValue;
import com.annimon.ownlang.lib.MapValue;
import com.annimon.ownlang.lib.NumberValue;
import com.annimon.ownlang.lib.Types;
import com.annimon.ownlang.lib.Value;
import java.util.regex.Pattern;
public class PatternValue extends MapValue {
private final Pattern value;
public PatternValue(Pattern value) {
super(8);
this.value = value;
init();
}
private void init() {
set("flags", Converters.voidToInt(value::flags));
set("pattern", Converters.voidToString(value::pattern));
set("matcher", this::matcher);
set("matches", this::matches);
set("split", this::split);
set("replaceCallback", this::replaceCallback);
}
public Pattern getValue() {
return value;
}
private Value split(Value[] args) {
Arguments.checkOrOr(1, 2, args.length);
final int limit = (args.length == 2) ? args[1].asInt() : 0;
return ArrayValue.of(value.split(args[0].asString(), limit));
}
private Value matcher(Value[] args) {
Arguments.check(1, args.length);
return new MatcherValue(value.matcher(args[0].asString()));
}
private Value matches(Value[] args) {
Arguments.check(1, args.length);
return NumberValue.fromBoolean(value.matcher(args[0].asString()).matches());
}
private Value replaceCallback(Value[] args) {
Arguments.check(2, args.length);
if (args[1].type() != Types.FUNCTION) {
throw new TypeException(args[1].toString() + " is not a function");
}
return MatcherValue.replaceCallback(
new MatcherValue(value.matcher(args[0].asString())),
((FunctionValue) args[1]).getValue());
}
}

View File

@ -0,0 +1,61 @@
package com.annimon.ownlang.modules.regex;
import com.annimon.ownlang.lib.Arguments;
import com.annimon.ownlang.lib.ArrayValue;
import com.annimon.ownlang.lib.Functions;
import com.annimon.ownlang.lib.MapValue;
import com.annimon.ownlang.lib.NumberValue;
import com.annimon.ownlang.lib.StringValue;
import com.annimon.ownlang.lib.Value;
import com.annimon.ownlang.lib.Variables;
import com.annimon.ownlang.modules.Module;
import java.util.regex.Pattern;
public final class regex implements Module {
public static void initConstants() {
MapValue map = new MapValue(20);
map.set("UNIX_LINES", NumberValue.of(Pattern.UNIX_LINES));
map.set("I", NumberValue.of(Pattern.CASE_INSENSITIVE));
map.set("CASE_INSENSITIVE", NumberValue.of(Pattern.CASE_INSENSITIVE));
map.set("COMMENTS", NumberValue.of(Pattern.COMMENTS));
map.set("M", NumberValue.of(Pattern.MULTILINE));
map.set("MULTILINE", NumberValue.of(Pattern.MULTILINE));
map.set("LITERAL", NumberValue.of(Pattern.LITERAL));
map.set("S", NumberValue.of(Pattern.DOTALL));
map.set("DOTALL", NumberValue.of(Pattern.DOTALL));
map.set("UNICODE_CASE", NumberValue.of(Pattern.UNICODE_CASE));
map.set("CANON_EQ", NumberValue.of(Pattern.CANON_EQ));
map.set("U", NumberValue.of(Pattern.UNICODE_CHARACTER_CLASS));
map.set("UNICODE_CHARACTER_CLASS", NumberValue.of(Pattern.UNICODE_CHARACTER_CLASS));
map.set("quote", args -> {
Arguments.check(1, args.length);
return new StringValue(Pattern.quote(args[0].asString()));
});
map.set("matches", args -> {
Arguments.check(2, args.length);
return NumberValue.fromBoolean(Pattern.matches(args[0].asString(), args[1].asString()));
});
map.set("split", args -> {
Arguments.checkOrOr(2, 3, args.length);
final Pattern pattern = Pattern.compile(args[0].asString());
final int limit = (args.length == 3) ? args[2].asInt() : 0;
return ArrayValue.of(pattern.split(args[1].asString(), limit));
});
map.set("compile", regex::compile);
Variables.define("Pattern", map);
}
@Override
public void init() {
initConstants();
Functions.set("regex", regex::compile);
}
private static Value compile(Value[] args) {
Arguments.checkOrOr(1, 2, args.length);
final int flags = (args.length == 2) ? args[1].asInt() : 0;
return new PatternValue(Pattern.compile(args[0].asString(), flags));
}
}

View File

@ -0,0 +1,15 @@
use ["regex", "types"]
def testMatchGitUrl() {
pattern = Pattern.compile("https?://((git(hu|la)b\.com)|bitbucket\.org)/?")
assertTrue(pattern.matches("http://github.com"))
assertTrue(pattern.matches("http://github.com/"))
assertTrue(pattern.matches("https://gitlab.com/"))
assertTrue(pattern.matches("https://bitbucket.org/"))
assertFalse(pattern.matches("http://github.org"))
assertFalse(pattern.matches("https://bithub.com/"))
assertFalse(pattern.matches("http://gitlab.org"))
assertFalse(pattern.matches("ftp://github.com/"))
assertFalse(pattern.matches("http://gitbucket.org/"))
}

View File

@ -0,0 +1,8 @@
use ["regex", "types"]
def testReplaceCallback() {
in = "[1-2-3-4]"
pattern = regex("(\d)")
out = pattern.replaceCallback(in, def(m) = m.group() * int(m.group()))
assertEquals("[1-22-333-4444]", out)
}