diff --git a/program.own b/program.own index c8c5f99..94c8796 100644 --- a/program.own +++ b/program.own @@ -185,4 +185,15 @@ println class.add(2, class.mul(2, 2)) println split("1/2/3/4/5/6", "/") println join(nums, ", ") println join(nums, "|", "/") -println join(nums, ", ", "[", "]") \ No newline at end of file +println join(nums, ", ", "[", "]") + +// Destructuring assignment +arr = ["a", "b", "c"] +extract(var1, var2, var3) = arr +echo(var1, var2, var3) +// Swap +extract(var2, var1) = [var1, var2] +echo(var1, var2) + +extract(, , var4) = arr +println var4 \ No newline at end of file diff --git a/src/com/annimon/ownlang/parser/Lexer.java b/src/com/annimon/ownlang/parser/Lexer.java index fe206b6..209ce44 100644 --- a/src/com/annimon/ownlang/parser/Lexer.java +++ b/src/com/annimon/ownlang/parser/Lexer.java @@ -74,6 +74,7 @@ public final class Lexer { KEYWORDS.put("use", TokenType.USE); KEYWORDS.put("match", TokenType.MATCH); KEYWORDS.put("case", TokenType.CASE); + KEYWORDS.put("extract", TokenType.EXTRACT); } private final String input; diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index ece352a..0f7849a 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -87,6 +87,9 @@ public final class Parser { if (match(TokenType.MATCH)) { return new ExprStatement(match()); } + if (match(TokenType.EXTRACT)) { + return destructuringAssignment(); + } if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { return new ExprStatement(function(qualifiedName())); } @@ -107,6 +110,22 @@ public final class Parser { throw new ParseException("Unknown statement: " + get(0)); } + private DestructuringAssignmentStatement destructuringAssignment() { + // extract(var1, var2, ...) = ... + consume(TokenType.LPAREN); + final List variables = new ArrayList<>(); + while (!match(TokenType.RPAREN)) { + if (lookMatch(0, TokenType.WORD)) { + variables.add(consume(TokenType.WORD).getText()); + } else { + variables.add(null); + } + match(TokenType.COMMA); + } + consume(TokenType.EQ); + return new DestructuringAssignmentStatement(variables, expression()); + } + private Statement ifElse() { final Expression condition = expression(); final Statement ifStatement = statementOrBlock(); diff --git a/src/com/annimon/ownlang/parser/TokenType.java b/src/com/annimon/ownlang/parser/TokenType.java index 1507c8b..05f80e4 100644 --- a/src/com/annimon/ownlang/parser/TokenType.java +++ b/src/com/annimon/ownlang/parser/TokenType.java @@ -26,6 +26,7 @@ public enum TokenType { USE, MATCH, CASE, + EXTRACT, PLUS, // + MINUS, // - diff --git a/src/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java b/src/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java new file mode 100644 index 0000000..2d100f4 --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/DestructuringAssignmentStatement.java @@ -0,0 +1,70 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.ArrayValue; +import com.annimon.ownlang.lib.MapValue; +import com.annimon.ownlang.lib.Types; +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.Variables; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * + * @author aNNiMON + */ +public final class DestructuringAssignmentStatement implements Statement { + + public final List variables; + public final Expression containerExpression; + + public DestructuringAssignmentStatement(List arguments, Expression container) { + this.variables = arguments; + this.containerExpression = container; + } + + @Override + public void execute() { + final Value container = containerExpression.eval(); + switch (container.type()) { + case Types.ARRAY: + execute((ArrayValue) container); + break; + case Types.MAP: + execute((MapValue) container); + break; + } + } + + private void execute(ArrayValue array) { + final int size = variables.size(); + for (int i = 0; i < size; i++) { + final String variable = variables.get(i); + if (variable != null) { + Variables.set(variable, array.get(i)); + } + } + } + private void execute(MapValue map) { + int i = 0; + for (Map.Entry entry : map) { + final String variable = variables.get(i); + if (variable != null) { + Variables.set(variable, new ArrayValue( + new Value[] { entry.getKey(), entry.getValue() } + )); + } + i++; + } + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + public String toString() { + return variables.toString(); + } +} diff --git a/src/com/annimon/ownlang/parser/ast/Visitor.java b/src/com/annimon/ownlang/parser/ast/Visitor.java index ab3356a..afb5899 100644 --- a/src/com/annimon/ownlang/parser/ast/Visitor.java +++ b/src/com/annimon/ownlang/parser/ast/Visitor.java @@ -16,6 +16,7 @@ public interface Visitor { void visit(ConditionalExpression s); void visit(ContinueStatement s); void visit(DoWhileStatement s); + void visit(DestructuringAssignmentStatement s); void visit(ForStatement s); void visit(ForeachArrayStatement s); void visit(ForeachMapStatement s); diff --git a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index 661c1ed..23e3c3d 100644 --- a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -61,6 +61,11 @@ public abstract class AbstractVisitor implements Visitor { @Override public void visit(ContinueStatement s) { } + + @Override + public void visit(DestructuringAssignmentStatement s) { + s.containerExpression.accept(this); + } @Override public void visit(DoWhileStatement s) {