mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 00:34:20 +03:00
Добавлен модуль regex
This commit is contained in:
parent
8965174589
commit
a30ca2fd4d
@ -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;
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
61
src/main/java/com/annimon/ownlang/modules/regex/regex.java
Normal file
61
src/main/java/com/annimon/ownlang/modules/regex/regex.java
Normal 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));
|
||||
}
|
||||
}
|
15
src/test/resources/modules/regex/match.own
Normal file
15
src/test/resources/modules/regex/match.own
Normal 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/"))
|
||||
}
|
8
src/test/resources/modules/regex/replaceCallback.own
Normal file
8
src/test/resources/modules/regex/replaceCallback.own
Normal 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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user