Pattern Matching для списков

This commit is contained in:
Victor 2016-02-13 15:19:59 +02:00
parent 32d40d9d04
commit f5c19e06d1
3 changed files with 128 additions and 0 deletions

View File

@ -205,3 +205,10 @@ println object1.arr
println object1.arr[0][1] println object1.arr[0][1]
object1.arr[0][1] = "str" object1.arr[0][1] = "str"
println object1.arr[0][1] println object1.arr[0][1]
println arrayRecursive([1, 2, 3, 4, 5, 6, 7])
def arrayRecursive(arr) = match arr {
case [head :: tail]: "[" + head + ", " + arrayRecursive(tail) + "]"
case []: "[]"
case last: "[" + last + ", []]"
}

View File

@ -264,25 +264,38 @@ public final class Parser {
MatchExpression.Pattern pattern = null; MatchExpression.Pattern pattern = null;
final Token current = get(0); final Token current = get(0);
if (match(TokenType.NUMBER)) { if (match(TokenType.NUMBER)) {
// case 0.5:
pattern = new MatchExpression.ConstantPattern( pattern = new MatchExpression.ConstantPattern(
new NumberValue(Double.parseDouble(current.getText())) new NumberValue(Double.parseDouble(current.getText()))
); );
} else if (match(TokenType.HEX_NUMBER)) { } else if (match(TokenType.HEX_NUMBER)) {
// case #FF:
pattern = new MatchExpression.ConstantPattern( pattern = new MatchExpression.ConstantPattern(
new NumberValue(Long.parseLong(current.getText(), 16)) new NumberValue(Long.parseLong(current.getText(), 16))
); );
} else if (match(TokenType.TEXT)) { } else if (match(TokenType.TEXT)) {
// case "text":
pattern = new MatchExpression.ConstantPattern( pattern = new MatchExpression.ConstantPattern(
new StringValue(current.getText()) new StringValue(current.getText())
); );
} else if (match(TokenType.WORD)) { } else if (match(TokenType.WORD)) {
// case value:
pattern = new MatchExpression.VariablePattern(current.getText()); pattern = new MatchExpression.VariablePattern(current.getText());
} else if (match(TokenType.LBRACKET)) {
// case [x :: xs]:
final MatchExpression.ListPattern listPattern = new MatchExpression.ListPattern();
while (!match(TokenType.RBRACKET)) {
listPattern.add(consume(TokenType.WORD).getText());
match(TokenType.COLONCOLON);
}
pattern = listPattern;
} }
if (pattern == null) { if (pattern == null) {
throw new ParseException("Wrong pattern in match expression: " + current); throw new ParseException("Wrong pattern in match expression: " + current);
} }
if (match(TokenType.IF)) { if (match(TokenType.IF)) {
// case e if e > 0:
pattern.optCondition = expression(); pattern.optCondition = expression();
} }

View File

@ -1,9 +1,12 @@
package com.annimon.ownlang.parser.ast; package com.annimon.ownlang.parser.ast;
import com.annimon.ownlang.exceptions.PatternMatchingException; import com.annimon.ownlang.exceptions.PatternMatchingException;
import com.annimon.ownlang.lib.ArrayValue;
import com.annimon.ownlang.lib.NumberValue; import com.annimon.ownlang.lib.NumberValue;
import com.annimon.ownlang.lib.Types;
import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Value;
import com.annimon.ownlang.lib.Variables; import com.annimon.ownlang.lib.Variables;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@ -48,10 +51,94 @@ public final class MatchExpression implements Expression {
Variables.remove(pattern.variable); Variables.remove(pattern.variable);
} }
} }
if ((value.type() == Types.ARRAY) && (p instanceof ListPattern)) {
final ListPattern pattern = (ListPattern) p;
if (matchListPattern((ArrayValue) value, pattern)) {
// Clean up variables if matched
final Value result = evalResult(p.result);
for (String var : pattern.parts) {
Variables.remove(var);
}
return result;
}
}
} }
throw new PatternMatchingException("No pattern were matched"); throw new PatternMatchingException("No pattern were matched");
} }
private boolean matchListPattern(ArrayValue array, ListPattern p) {
final List<String> parts = p.parts;
final int partsSize = parts.size();
final int arraySize = array.size();
switch (partsSize) {
case 0: // match [] { case []: ... }
if ((arraySize == 0) && optMatches(p)) {
return true;
}
return false;
case 1: // match arr { case [x]: x = arr ... }
final String variable = parts.get(0);
Variables.set(variable, array);
if (optMatches(p)) {
return true;
}
Variables.remove(variable);
return false;
default: { // match arr { case [...]: .. }
if (partsSize == arraySize) {
// match [0, 1, 2] { case [a::b::c]: a=0, b=1, c=2 ... }
return matchListPatternEqualsSize(p, parts, partsSize, array);
} else if (partsSize < arraySize) {
// match [1, 2, 3] { case [head :: tail]: ... }
return matchListPatternWithTail(p, parts, partsSize, array, arraySize);
}
return false;
}
}
}
private boolean matchListPatternEqualsSize(ListPattern p, List<String> parts, int partsSize, ArrayValue array) {
// Set variables
for (int i = 0; i < partsSize; i++) {
Variables.set(parts.get(i), array.get(i));
}
if (optMatches(p)) {
// Clean up will be provided after evaluate result
return true;
}
// Clean up variables if no match
for (String var : parts) {
Variables.remove(var);
}
return false;
}
private boolean matchListPatternWithTail(ListPattern p, List<String> parts, int partsSize, ArrayValue array, int arraySize) {
// Set element variables
final int lastPart = partsSize - 1;
for (int i = 0; i < lastPart; i++) {
Variables.set(parts.get(i), array.get(i));
}
// Set tail variable
final ArrayValue tail = new ArrayValue(arraySize - partsSize + 1);
for (int i = lastPart; i < arraySize; i++) {
tail.set(i - lastPart, array.get(i));
}
Variables.set(parts.get(lastPart), tail);
// Check optional condition
if (optMatches(p)) {
// Clean up will be provided after evaluate result
return true;
}
// Clean up variables
for (String var : parts) {
Variables.remove(var);
}
return false;
}
private boolean match(Value value, Value constant) { private boolean match(Value value, Value constant) {
if (value.type() != constant.type()) return false; if (value.type() != constant.type()) return false;
return value.equals(constant); return value.equals(constant);
@ -117,4 +204,25 @@ public final class MatchExpression implements Expression {
return variable + ": " + result; return variable + ": " + result;
} }
} }
public static class ListPattern extends Pattern {
public List<String> parts;
public ListPattern() {
this(new ArrayList<String>());
}
public ListPattern(List<String> parts) {
this.parts = parts;
}
public void add(String part) {
parts.add(part);
}
@Override
public String toString() {
return parts + ": " + result;
}
}
} }