Skip to content

Commit a30ca2f

Browse files
committed
Добавлен модуль regex
1 parent 8965174 commit a30ca2f

File tree

5 files changed

+317
-0
lines changed

5 files changed

+317
-0
lines changed
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package com.annimon.ownlang.modules.regex;
2+
3+
import com.annimon.ownlang.exceptions.TypeException;
4+
import com.annimon.ownlang.lib.*;
5+
import java.util.regex.Matcher;
6+
7+
public class MatcherValue extends MapValue {
8+
9+
private final Matcher value;
10+
11+
public MatcherValue(Matcher value) {
12+
super(22);
13+
this.value = value;
14+
init();
15+
}
16+
17+
private void init() {
18+
set("start", this::start);
19+
set("end", this::end);
20+
set("find", this::find);
21+
set("group", this::group);
22+
set("pattern", this::pattern);
23+
set("region", this::region);
24+
set("replaceFirst", this::replaceFirst);
25+
set("replaceAll", this::replaceAll);
26+
set("replaceCallback", this::replaceCallback);
27+
set("reset", this::reset);
28+
set("usePattern", this::usePattern);
29+
set("useAnchoringBounds", this::useAnchoringBounds);
30+
set("hasAnchoringBounds", Converters.voidToBoolean(value::hasAnchoringBounds));
31+
set("useTransparentBounds", this::useTransparentBounds);
32+
set("hasTransparentBounds", Converters.voidToBoolean(value::hasTransparentBounds));
33+
set("hitEnd", Converters.voidToBoolean(value::hitEnd));
34+
set("lookingAt", Converters.voidToBoolean(value::lookingAt));
35+
set("matches", Converters.voidToBoolean(value::matches));
36+
set("groupCount", Converters.voidToInt(value::groupCount));
37+
set("regionStart", Converters.voidToInt(value::regionStart));
38+
set("regionEnd", Converters.voidToInt(value::regionEnd));
39+
}
40+
41+
private Value start(Value[] args) {
42+
Arguments.checkOrOr(0, 1, args.length);
43+
final int result;
44+
if (args.length == 0) {
45+
result = value.start();
46+
} else {
47+
final Value arg = args[0];
48+
if (arg.type() == Types.NUMBER) {
49+
result = value.start(arg.asInt());
50+
} else {
51+
result = value.start(arg.asString());
52+
}
53+
}
54+
return NumberValue.of(result);
55+
}
56+
57+
private Value end(Value[] args) {
58+
Arguments.checkOrOr(0, 1, args.length);
59+
final int result;
60+
if (args.length == 0) {
61+
result = value.end();
62+
} else {
63+
final Value arg = args[0];
64+
if (arg.type() == Types.NUMBER) {
65+
result = value.end(arg.asInt());
66+
} else {
67+
result = value.end(arg.asString());
68+
}
69+
}
70+
return NumberValue.of(result);
71+
}
72+
73+
private Value find(Value[] args) {
74+
Arguments.checkOrOr(0, 1, args.length);
75+
final boolean result;
76+
if (args.length == 0) {
77+
result = value.find();
78+
} else {
79+
result = value.find(args[0].asInt());
80+
}
81+
return NumberValue.fromBoolean(result);
82+
}
83+
84+
private Value group(Value[] args) {
85+
Arguments.checkOrOr(0, 1, args.length);
86+
final String result;
87+
if (args.length == 0) {
88+
result = value.group();
89+
} else {
90+
final Value arg = args[0];
91+
if (arg.type() == Types.NUMBER) {
92+
result = value.group(arg.asInt());
93+
} else {
94+
result = value.group(arg.asString());
95+
}
96+
}
97+
return new StringValue(result);
98+
}
99+
100+
private Value pattern(Value[] args) {
101+
return new PatternValue(value.pattern());
102+
}
103+
104+
private Value region(Value[] args) {
105+
Arguments.check(2, args.length);
106+
value.region(args[0].asInt(), args[1].asInt());
107+
return this;
108+
}
109+
110+
private Value replaceFirst(Value[] args) {
111+
Arguments.check(1, args.length);
112+
return new StringValue(value.replaceFirst(args[0].asString()));
113+
}
114+
115+
private Value replaceAll(Value[] args) {
116+
Arguments.check(1, args.length);
117+
return new StringValue(value.replaceAll(args[0].asString()));
118+
}
119+
120+
static StringValue replaceCallback(MatcherValue matcherValue, Function callback) {
121+
final Matcher matcher = matcherValue.value;
122+
final StringBuffer sb = new StringBuffer();
123+
while (matcher.find()) {
124+
String replacement = callback.execute(matcherValue).asString();
125+
matcher.appendReplacement(sb, replacement);
126+
}
127+
matcher.appendTail(sb);
128+
return new StringValue(sb.toString());
129+
}
130+
131+
private Value replaceCallback(Value[] args) {
132+
Arguments.check(1, args.length);
133+
if (args[0].type() != Types.FUNCTION) {
134+
throw new TypeException(args[0].toString() + " is not a function");
135+
}
136+
return replaceCallback(this, ((FunctionValue) args[0]).getValue());
137+
}
138+
139+
private Value reset(Value[] args) {
140+
Arguments.checkOrOr(0, 1, args.length);
141+
if (args.length == 0) {
142+
value.reset();
143+
} else {
144+
value.reset(args[0].asString());
145+
}
146+
return this;
147+
}
148+
149+
private Value useAnchoringBounds(Value[] args) {
150+
Arguments.check(1, args.length);
151+
value.useAnchoringBounds(args[0].asInt() != 0);
152+
return this;
153+
}
154+
155+
private Value useTransparentBounds(Value[] args) {
156+
Arguments.check(1, args.length);
157+
value.useTransparentBounds(args[0].asInt() != 0);
158+
return this;
159+
}
160+
161+
private Value usePattern(Value[] args) {
162+
Arguments.check(1, args.length);
163+
final Value arg = args[0];
164+
if (arg.type() == Types.MAP && (arg instanceof PatternValue)) {
165+
value.usePattern(((PatternValue) arg).getValue());
166+
} else {
167+
throw new TypeException("Pattern value expected");
168+
}
169+
return this;
170+
}
171+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.annimon.ownlang.modules.regex;
2+
3+
import com.annimon.ownlang.exceptions.TypeException;
4+
import com.annimon.ownlang.lib.Arguments;
5+
import com.annimon.ownlang.lib.ArrayValue;
6+
import com.annimon.ownlang.lib.Converters;
7+
import com.annimon.ownlang.lib.FunctionValue;
8+
import com.annimon.ownlang.lib.MapValue;
9+
import com.annimon.ownlang.lib.NumberValue;
10+
import com.annimon.ownlang.lib.Types;
11+
import com.annimon.ownlang.lib.Value;
12+
import java.util.regex.Pattern;
13+
14+
public class PatternValue extends MapValue {
15+
16+
private final Pattern value;
17+
18+
public PatternValue(Pattern value) {
19+
super(8);
20+
this.value = value;
21+
init();
22+
}
23+
24+
private void init() {
25+
set("flags", Converters.voidToInt(value::flags));
26+
set("pattern", Converters.voidToString(value::pattern));
27+
set("matcher", this::matcher);
28+
set("matches", this::matches);
29+
set("split", this::split);
30+
set("replaceCallback", this::replaceCallback);
31+
}
32+
33+
public Pattern getValue() {
34+
return value;
35+
}
36+
37+
private Value split(Value[] args) {
38+
Arguments.checkOrOr(1, 2, args.length);
39+
final int limit = (args.length == 2) ? args[1].asInt() : 0;
40+
return ArrayValue.of(value.split(args[0].asString(), limit));
41+
}
42+
43+
private Value matcher(Value[] args) {
44+
Arguments.check(1, args.length);
45+
return new MatcherValue(value.matcher(args[0].asString()));
46+
}
47+
48+
private Value matches(Value[] args) {
49+
Arguments.check(1, args.length);
50+
return NumberValue.fromBoolean(value.matcher(args[0].asString()).matches());
51+
}
52+
53+
private Value replaceCallback(Value[] args) {
54+
Arguments.check(2, args.length);
55+
if (args[1].type() != Types.FUNCTION) {
56+
throw new TypeException(args[1].toString() + " is not a function");
57+
}
58+
return MatcherValue.replaceCallback(
59+
new MatcherValue(value.matcher(args[0].asString())),
60+
((FunctionValue) args[1]).getValue());
61+
}
62+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.annimon.ownlang.modules.regex;
2+
3+
import com.annimon.ownlang.lib.Arguments;
4+
import com.annimon.ownlang.lib.ArrayValue;
5+
import com.annimon.ownlang.lib.Functions;
6+
import com.annimon.ownlang.lib.MapValue;
7+
import com.annimon.ownlang.lib.NumberValue;
8+
import com.annimon.ownlang.lib.StringValue;
9+
import com.annimon.ownlang.lib.Value;
10+
import com.annimon.ownlang.lib.Variables;
11+
import com.annimon.ownlang.modules.Module;
12+
import java.util.regex.Pattern;
13+
14+
public final class regex implements Module {
15+
16+
public static void initConstants() {
17+
MapValue map = new MapValue(20);
18+
map.set("UNIX_LINES", NumberValue.of(Pattern.UNIX_LINES));
19+
map.set("I", NumberValue.of(Pattern.CASE_INSENSITIVE));
20+
map.set("CASE_INSENSITIVE", NumberValue.of(Pattern.CASE_INSENSITIVE));
21+
map.set("COMMENTS", NumberValue.of(Pattern.COMMENTS));
22+
map.set("M", NumberValue.of(Pattern.MULTILINE));
23+
map.set("MULTILINE", NumberValue.of(Pattern.MULTILINE));
24+
map.set("LITERAL", NumberValue.of(Pattern.LITERAL));
25+
map.set("S", NumberValue.of(Pattern.DOTALL));
26+
map.set("DOTALL", NumberValue.of(Pattern.DOTALL));
27+
map.set("UNICODE_CASE", NumberValue.of(Pattern.UNICODE_CASE));
28+
map.set("CANON_EQ", NumberValue.of(Pattern.CANON_EQ));
29+
map.set("U", NumberValue.of(Pattern.UNICODE_CHARACTER_CLASS));
30+
map.set("UNICODE_CHARACTER_CLASS", NumberValue.of(Pattern.UNICODE_CHARACTER_CLASS));
31+
32+
map.set("quote", args -> {
33+
Arguments.check(1, args.length);
34+
return new StringValue(Pattern.quote(args[0].asString()));
35+
});
36+
map.set("matches", args -> {
37+
Arguments.check(2, args.length);
38+
return NumberValue.fromBoolean(Pattern.matches(args[0].asString(), args[1].asString()));
39+
});
40+
map.set("split", args -> {
41+
Arguments.checkOrOr(2, 3, args.length);
42+
final Pattern pattern = Pattern.compile(args[0].asString());
43+
final int limit = (args.length == 3) ? args[2].asInt() : 0;
44+
return ArrayValue.of(pattern.split(args[1].asString(), limit));
45+
});
46+
map.set("compile", regex::compile);
47+
Variables.define("Pattern", map);
48+
}
49+
50+
@Override
51+
public void init() {
52+
initConstants();
53+
Functions.set("regex", regex::compile);
54+
}
55+
56+
private static Value compile(Value[] args) {
57+
Arguments.checkOrOr(1, 2, args.length);
58+
final int flags = (args.length == 2) ? args[1].asInt() : 0;
59+
return new PatternValue(Pattern.compile(args[0].asString(), flags));
60+
}
61+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use ["regex", "types"]
2+
3+
def testMatchGitUrl() {
4+
pattern = Pattern.compile("https?://((git(hu|la)b\.com)|bitbucket\.org)/?")
5+
assertTrue(pattern.matches("http://github.com"))
6+
assertTrue(pattern.matches("http://github.com/"))
7+
assertTrue(pattern.matches("https://gitlab.com/"))
8+
assertTrue(pattern.matches("https://bitbucket.org/"))
9+
10+
assertFalse(pattern.matches("http://github.org"))
11+
assertFalse(pattern.matches("https://bithub.com/"))
12+
assertFalse(pattern.matches("http://gitlab.org"))
13+
assertFalse(pattern.matches("ftp://github.com/"))
14+
assertFalse(pattern.matches("http://gitbucket.org/"))
15+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
use ["regex", "types"]
2+
3+
def testReplaceCallback() {
4+
in = "[1-2-3-4]"
5+
pattern = regex("(\d)")
6+
out = pattern.replaceCallback(in, def(m) = m.group() * int(m.group()))
7+
assertEquals("[1-22-333-4444]", out)
8+
}

0 commit comments

Comments
 (0)