diff --git a/program.own b/program.own index e87bb27..cdf16cb 100644 --- a/program.own +++ b/program.own @@ -101,3 +101,13 @@ print arr1 print "\n" print arr1 << arr2 +for op, func : map { + echo (4, op, 5, "=", func(4,5)) +} + +for v : arr1 print "" + v + ", " +print "\n" +for (v : arr1 << arr2) print "" + v + ", " +print "\n" +for v : [1,2,3,4,5,6,7,8,9] print "" + v + ", " + diff --git a/src/com/annimon/ownlang/parser/Parser.java b/src/com/annimon/ownlang/parser/Parser.java index 29e06d0..eb01a7b 100644 --- a/src/com/annimon/ownlang/parser/Parser.java +++ b/src/com/annimon/ownlang/parser/Parser.java @@ -124,17 +124,50 @@ public final class Parser { } private Statement forStatement() { - match(TokenType.LPAREN); // необязательные скобки + int foreachIndex = lookMatch(0, TokenType.LPAREN) ? 1 : 0; + if (lookMatch(foreachIndex, TokenType.WORD) && lookMatch(foreachIndex + 1, TokenType.COLON)) { + // for v : arr || for (v : arr) + return foreachArrayStatement(); + } + if (lookMatch(foreachIndex, TokenType.WORD) && lookMatch(foreachIndex + 1, TokenType.COMMA) + && lookMatch(foreachIndex + 2, TokenType.WORD) && lookMatch(foreachIndex + 3, TokenType.COLON)) { + // for key, value : arr || for (key, value : arr) + return foreachMapStatement(); + } + + boolean openParen = match(TokenType.LPAREN); // необязательные скобки final Statement initialization = assignmentStatement(); consume(TokenType.COMMA); final Expression termination = expression(); consume(TokenType.COMMA); final Statement increment = assignmentStatement(); - match(TokenType.RPAREN); // необязательные скобки + if (openParen) consume(TokenType.RPAREN); // скобки final Statement statement = statementOrBlock(); return new ForStatement(initialization, termination, increment, statement); } + private ForeachArrayStatement foreachArrayStatement() { + boolean openParen = match(TokenType.LPAREN); // необязательные скобки + final String variable = consume(TokenType.WORD).getText(); + consume(TokenType.COLON); + final Expression container = expression(); + if (openParen) consume(TokenType.RPAREN); // скобки + final Statement statement = statementOrBlock(); + return new ForeachArrayStatement(variable, container, statement); + } + + private ForeachMapStatement foreachMapStatement() { + boolean openParen = match(TokenType.LPAREN); // необязательные скобки + final String key = consume(TokenType.WORD).getText(); + consume(TokenType.COMMA); + final String value = consume(TokenType.WORD).getText(); + consume(TokenType.COLON); + final Expression container = expression(); + if (openParen) consume(TokenType.RPAREN); // скобки + final Statement statement = statementOrBlock(); + return new ForeachMapStatement(key, value, container, statement); + } + private FunctionDefineStatement functionDefine() { final String name = consume(TokenType.WORD).getText(); consume(TokenType.LPAREN); diff --git a/src/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java b/src/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java new file mode 100644 index 0000000..c80e009 --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/ForeachArrayStatement.java @@ -0,0 +1,51 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.Variables; + +/** + * + * @author aNNiMON + */ +public final class ForeachArrayStatement implements Statement { + + public final String variable; + public final Expression container; + public final Statement body; + + public ForeachArrayStatement(String variable, Expression container, Statement body) { + this.variable = variable; + this.container = container; + this.body = body; + } + + @Override + public void execute() { + final Value previousVariableValue = Variables.isExists(variable) ? Variables.get(variable) : null; + final Iterable iterator = (Iterable) container.eval(); + for (Value value : iterator) { + Variables.set(variable, value); + try { + body.execute(); + } catch (BreakStatement bs) { + break; + } catch (ContinueStatement cs) { + // continue; + } + } + // Восстанавливаем переменную + if (previousVariableValue != null) { + Variables.set(variable, previousVariableValue); + } + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + public String toString() { + return String.format("for %s : %s %s", variable, container, body); + } +} diff --git a/src/com/annimon/ownlang/parser/ast/ForeachMapStatement.java b/src/com/annimon/ownlang/parser/ast/ForeachMapStatement.java new file mode 100644 index 0000000..ee9bc4b --- /dev/null +++ b/src/com/annimon/ownlang/parser/ast/ForeachMapStatement.java @@ -0,0 +1,58 @@ +package com.annimon.ownlang.parser.ast; + +import com.annimon.ownlang.lib.Value; +import com.annimon.ownlang.lib.Variables; +import java.util.Map; + +/** + * + * @author aNNiMON + */ +public final class ForeachMapStatement implements Statement { + + public final String key, value; + public final Expression container; + public final Statement body; + + public ForeachMapStatement(String key, String value, Expression container, Statement body) { + this.key = key; + this.value = value; + this.container = container; + this.body = body; + } + + @Override + public void execute() { + final Value previousVariableValue1 = Variables.isExists(key) ? Variables.get(key) : null; + final Value previousVariableValue2 = Variables.isExists(value) ? Variables.get(value) : null; + final Iterable> iterator = (Iterable>) container.eval(); + for (Map.Entry entry : iterator) { + Variables.set(key, entry.getKey()); + Variables.set(value, entry.getValue()); + try { + body.execute(); + } catch (BreakStatement bs) { + break; + } catch (ContinueStatement cs) { + // continue; + } + } + // Восстанавливаем переменные + if (previousVariableValue1 != null) { + Variables.set(key, previousVariableValue1); + } + if (previousVariableValue2 != null) { + Variables.set(value, previousVariableValue2); + } + } + + @Override + public void accept(Visitor visitor) { + visitor.visit(this); + } + + @Override + public String toString() { + return String.format("for %s, %s : %s %s", key, value, container, body); + } +} diff --git a/src/com/annimon/ownlang/parser/ast/Visitor.java b/src/com/annimon/ownlang/parser/ast/Visitor.java index 941609d..8e80eaf 100644 --- a/src/com/annimon/ownlang/parser/ast/Visitor.java +++ b/src/com/annimon/ownlang/parser/ast/Visitor.java @@ -17,6 +17,8 @@ public interface Visitor { void visit(ContinueStatement s); void visit(DoWhileStatement s); void visit(ForStatement s); + void visit(ForeachArrayStatement s); + void visit(ForeachMapStatement s); void visit(FunctionDefineStatement s); void visit(FunctionReferenceExpression e); void visit(FunctionStatement s); diff --git a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java index a87c0c4..fc936f4 100644 --- a/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java +++ b/src/com/annimon/ownlang/parser/visitors/AbstractVisitor.java @@ -75,6 +75,18 @@ public abstract class AbstractVisitor implements Visitor { s.increment.accept(this); s.statement.accept(this); } + + @Override + public void visit(ForeachArrayStatement s) { + s.container.accept(this); + s.body.accept(this); + } + + @Override + public void visit(ForeachMapStatement s) { + s.container.accept(this); + s.body.accept(this); + } @Override public void visit(FunctionDefineStatement s) {