Skip to content

Commit f5c19e0

Browse files
committed
Pattern Matching для списков
1 parent 32d40d9 commit f5c19e0

File tree

3 files changed

+128
-0
lines changed

3 files changed

+128
-0
lines changed

program.own

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,10 @@ println object1.arr
205205
println object1.arr[0][1]
206206
object1.arr[0][1] = "str"
207207
println object1.arr[0][1]
208+
209+
println arrayRecursive([1, 2, 3, 4, 5, 6, 7])
210+
def arrayRecursive(arr) = match arr {
211+
case [head :: tail]: "[" + head + ", " + arrayRecursive(tail) + "]"
212+
case []: "[]"
213+
case last: "[" + last + ", []]"
214+
}

src/com/annimon/ownlang/parser/Parser.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,25 +264,38 @@ private MatchExpression match() {
264264
MatchExpression.Pattern pattern = null;
265265
final Token current = get(0);
266266
if (match(TokenType.NUMBER)) {
267+
// case 0.5:
267268
pattern = new MatchExpression.ConstantPattern(
268269
new NumberValue(Double.parseDouble(current.getText()))
269270
);
270271
} else if (match(TokenType.HEX_NUMBER)) {
272+
// case #FF:
271273
pattern = new MatchExpression.ConstantPattern(
272274
new NumberValue(Long.parseLong(current.getText(), 16))
273275
);
274276
} else if (match(TokenType.TEXT)) {
277+
// case "text":
275278
pattern = new MatchExpression.ConstantPattern(
276279
new StringValue(current.getText())
277280
);
278281
} else if (match(TokenType.WORD)) {
282+
// case value:
279283
pattern = new MatchExpression.VariablePattern(current.getText());
284+
} else if (match(TokenType.LBRACKET)) {
285+
// case [x :: xs]:
286+
final MatchExpression.ListPattern listPattern = new MatchExpression.ListPattern();
287+
while (!match(TokenType.RBRACKET)) {
288+
listPattern.add(consume(TokenType.WORD).getText());
289+
match(TokenType.COLONCOLON);
290+
}
291+
pattern = listPattern;
280292
}
281293

282294
if (pattern == null) {
283295
throw new ParseException("Wrong pattern in match expression: " + current);
284296
}
285297
if (match(TokenType.IF)) {
298+
// case e if e > 0:
286299
pattern.optCondition = expression();
287300
}
288301

src/com/annimon/ownlang/parser/ast/MatchExpression.java

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package com.annimon.ownlang.parser.ast;
22

33
import com.annimon.ownlang.exceptions.PatternMatchingException;
4+
import com.annimon.ownlang.lib.ArrayValue;
45
import com.annimon.ownlang.lib.NumberValue;
6+
import com.annimon.ownlang.lib.Types;
57
import com.annimon.ownlang.lib.Value;
68
import com.annimon.ownlang.lib.Variables;
9+
import java.util.ArrayList;
710
import java.util.List;
811

912
/**
@@ -48,10 +51,94 @@ public Value eval() {
4851
Variables.remove(pattern.variable);
4952
}
5053
}
54+
if ((value.type() == Types.ARRAY) && (p instanceof ListPattern)) {
55+
final ListPattern pattern = (ListPattern) p;
56+
if (matchListPattern((ArrayValue) value, pattern)) {
57+
// Clean up variables if matched
58+
final Value result = evalResult(p.result);
59+
for (String var : pattern.parts) {
60+
Variables.remove(var);
61+
}
62+
return result;
63+
}
64+
}
5165
}
5266
throw new PatternMatchingException("No pattern were matched");
5367
}
5468

69+
private boolean matchListPattern(ArrayValue array, ListPattern p) {
70+
final List<String> parts = p.parts;
71+
final int partsSize = parts.size();
72+
final int arraySize = array.size();
73+
switch (partsSize) {
74+
case 0: // match [] { case []: ... }
75+
if ((arraySize == 0) && optMatches(p)) {
76+
return true;
77+
}
78+
return false;
79+
80+
case 1: // match arr { case [x]: x = arr ... }
81+
final String variable = parts.get(0);
82+
Variables.set(variable, array);
83+
if (optMatches(p)) {
84+
return true;
85+
}
86+
Variables.remove(variable);
87+
return false;
88+
89+
default: { // match arr { case [...]: .. }
90+
if (partsSize == arraySize) {
91+
// match [0, 1, 2] { case [a::b::c]: a=0, b=1, c=2 ... }
92+
return matchListPatternEqualsSize(p, parts, partsSize, array);
93+
} else if (partsSize < arraySize) {
94+
// match [1, 2, 3] { case [head :: tail]: ... }
95+
return matchListPatternWithTail(p, parts, partsSize, array, arraySize);
96+
}
97+
return false;
98+
}
99+
}
100+
}
101+
102+
private boolean matchListPatternEqualsSize(ListPattern p, List<String> parts, int partsSize, ArrayValue array) {
103+
// Set variables
104+
for (int i = 0; i < partsSize; i++) {
105+
Variables.set(parts.get(i), array.get(i));
106+
}
107+
if (optMatches(p)) {
108+
// Clean up will be provided after evaluate result
109+
return true;
110+
}
111+
// Clean up variables if no match
112+
for (String var : parts) {
113+
Variables.remove(var);
114+
}
115+
return false;
116+
}
117+
118+
private boolean matchListPatternWithTail(ListPattern p, List<String> parts, int partsSize, ArrayValue array, int arraySize) {
119+
// Set element variables
120+
final int lastPart = partsSize - 1;
121+
for (int i = 0; i < lastPart; i++) {
122+
Variables.set(parts.get(i), array.get(i));
123+
}
124+
// Set tail variable
125+
final ArrayValue tail = new ArrayValue(arraySize - partsSize + 1);
126+
for (int i = lastPart; i < arraySize; i++) {
127+
tail.set(i - lastPart, array.get(i));
128+
}
129+
Variables.set(parts.get(lastPart), tail);
130+
// Check optional condition
131+
if (optMatches(p)) {
132+
// Clean up will be provided after evaluate result
133+
return true;
134+
}
135+
// Clean up variables
136+
for (String var : parts) {
137+
Variables.remove(var);
138+
}
139+
return false;
140+
}
141+
55142
private boolean match(Value value, Value constant) {
56143
if (value.type() != constant.type()) return false;
57144
return value.equals(constant);
@@ -117,4 +204,25 @@ public String toString() {
117204
return variable + ": " + result;
118205
}
119206
}
207+
208+
public static class ListPattern extends Pattern {
209+
public List<String> parts;
210+
211+
public ListPattern() {
212+
this(new ArrayList<String>());
213+
}
214+
215+
public ListPattern(List<String> parts) {
216+
this.parts = parts;
217+
}
218+
219+
public void add(String part) {
220+
parts.add(part);
221+
}
222+
223+
@Override
224+
public String toString() {
225+
return parts + ": " + result;
226+
}
227+
}
120228
}

0 commit comments

Comments
 (0)