From f5c19e06d1788d0a723552c735c02c31b891f072 Mon Sep 17 00:00:00 2001 From: Victor Date: Sat, 13 Feb 2016 15:19:59 +0200 Subject: [PATCH] =?UTF-8?q?Pattern=20Matching=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D1=81=D0=BF=D0=B8=D1=81=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- program.own | 7 ++ src/com/annimon/ownlang/parser/Parser.java | 13 +++ .../ownlang/parser/ast/MatchExpression.java | 108 ++++++++++++++++++ 3 files changed, 128 insertions(+) diff --git a/program.own b/program.own index 2244573..67bf576 100644 --- a/program.own +++ b/program.own @@ -205,3 +205,10 @@ println object1.arr println object1.arr[0][1] object1.arr[0][1] = "str" 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 + ", []]" +} \ No newline at end of file diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index 367ddf1..fc68b9b 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -264,25 +264,38 @@ public final class Parser { MatchExpression.Pattern pattern = null; final Token current = get(0); if (match(TokenType.NUMBER)) { + // case 0.5: pattern = new MatchExpression.ConstantPattern( new NumberValue(Double.parseDouble(current.getText())) ); } else if (match(TokenType.HEX_NUMBER)) { + // case #FF: pattern = new MatchExpression.ConstantPattern( new NumberValue(Long.parseLong(current.getText(), 16)) ); } else if (match(TokenType.TEXT)) { + // case "text": pattern = new MatchExpression.ConstantPattern( new StringValue(current.getText()) ); } else if (match(TokenType.WORD)) { + // case value: 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) { throw new ParseException("Wrong pattern in match expression: " + current); } if (match(TokenType.IF)) { + // case e if e > 0: pattern.optCondition = expression(); } diff --git a/src/com/annimon/ownlang/parser/ast/MatchExpression.java b/src/com/annimon/ownlang/parser/ast/MatchExpression.java index 877887d..7b8634b 100644 --- a/src/com/annimon/ownlang/parser/ast/MatchExpression.java +++ b/src/com/annimon/ownlang/parser/ast/MatchExpression.java @@ -1,9 +1,12 @@ package com.annimon.ownlang.parser.ast; import com.annimon.ownlang.exceptions.PatternMatchingException; +import com.annimon.ownlang.lib.ArrayValue; import com.annimon.ownlang.lib.NumberValue; +import com.annimon.ownlang.lib.Types; import com.annimon.ownlang.lib.Value; import com.annimon.ownlang.lib.Variables; +import java.util.ArrayList; import java.util.List; /** @@ -48,10 +51,94 @@ public final class MatchExpression implements Expression { 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"); } + private boolean matchListPattern(ArrayValue array, ListPattern p) { + final List 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 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 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) { if (value.type() != constant.type()) return false; return value.equals(constant); @@ -117,4 +204,25 @@ public final class MatchExpression implements Expression { return variable + ": " + result; } } + + public static class ListPattern extends Pattern { + public List parts; + + public ListPattern() { + this(new ArrayList()); + } + + public ListPattern(List parts) { + this.parts = parts; + } + + public void add(String part) { + parts.add(part); + } + + @Override + public String toString() { + return parts + ": " + result; + } + } }