Улучшен доступ к вложенным контейнерам

This commit is contained in:
Victor 2016-02-13 12:01:11 +02:00
parent 9536c7f5b0
commit 32d40d9d04
8 changed files with 143 additions and 118 deletions

View File

@ -197,3 +197,11 @@ echo(var1, var2)
extract(, , var4) = arr extract(, , var4) = arr
println var4 println var4
array1 = [[1, 2], [3], [4, 5], [6]]
println array1[0][1]
object1 = {"arr": array1}
println object1.arr
println object1.arr[0][1]
object1.arr[0][1] = "str"
println object1.arr[0][1]

View File

@ -102,10 +102,12 @@ public final class Parser {
consume(TokenType.EQ); consume(TokenType.EQ);
return new AssignmentStatement(variable, expression()); return new AssignmentStatement(variable, expression());
} }
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LBRACKET)) {
final ArrayAccessExpression array = element(); final Expression qualifiedNameExpr = qualifiedName();
if (lookMatch(0, TokenType.EQ) && (qualifiedNameExpr instanceof ContainerAccessExpression)) {
consume(TokenType.EQ); consume(TokenType.EQ);
return new ArrayAssignmentStatement(array, expression()); final ContainerAccessExpression containerExpr = (ContainerAccessExpression) qualifiedNameExpr;
return new ContainerAssignmentStatement(containerExpr, expression());
} }
throw new ParseException("Unknown statement: " + get(0)); throw new ParseException("Unknown statement: " + get(0));
} }
@ -249,31 +251,6 @@ public final class Parser {
return new MapExpression(elements); return new MapExpression(elements);
} }
private ArrayAccessExpression element() {
// array[e1][e2]...[eN]
final String variable = consume(TokenType.WORD).getText();
final List<Expression> indices = new ArrayList<>();
do {
consume(TokenType.LBRACKET);
indices.add(expression());
consume(TokenType.RBRACKET);
} while(lookMatch(0, TokenType.LBRACKET));
return new ArrayAccessExpression(variable, indices);
}
private ArrayAccessExpression object() {
// object.field1.field2
// Syntaxic sugar for object["field1"]["field2"]
final String variable = consume(TokenType.WORD).getText();
final List<Expression> indices = new ArrayList<>();
while (match(TokenType.DOT)) {
final String fieldName = consume(TokenType.WORD).getText();
final Expression key = new ValueExpression(fieldName);
indices.add(key);
}
return new ArrayAccessExpression(variable, indices);
}
private MatchExpression match() { private MatchExpression match() {
// match expression { // match expression {
// case pattern1: result1 // case pattern1: result1
@ -563,9 +540,11 @@ public final class Parser {
} }
private Expression variable() { private Expression variable() {
// function(...
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) { if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) {
return function(new ValueExpression(consume(TokenType.WORD).getText())); return function(new ValueExpression(consume(TokenType.WORD).getText()));
} }
final Expression qualifiedNameExpr = qualifiedName(); final Expression qualifiedNameExpr = qualifiedName();
if (qualifiedNameExpr != null) { if (qualifiedNameExpr != null) {
// variable(args) || arr["key"](args) || obj.key(args) // variable(args) || arr["key"](args) || obj.key(args)
@ -585,17 +564,35 @@ public final class Parser {
} }
private Expression qualifiedName() { private Expression qualifiedName() {
// var || var.key[index].key2
final Token current = get(0); final Token current = get(0);
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LBRACKET)) { if (!match(TokenType.WORD)) return null;
return element();
} final List<Expression> indices = variableSuffix();
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.DOT)) { if ((indices == null) || indices.isEmpty()) {
return object();
}
if (match(TokenType.WORD)) {
return new VariableExpression(current.getText()); return new VariableExpression(current.getText());
} }
return null; return new ContainerAccessExpression(current.getText(), indices);
}
private List<Expression> variableSuffix() {
// .key1.arr1[expr1][expr2].key2
if (!lookMatch(0, TokenType.DOT) && !lookMatch(0, TokenType.LBRACKET)) {
return null;
}
final List<Expression> indices = new ArrayList<>();
while (lookMatch(0, TokenType.DOT) || lookMatch(0, TokenType.LBRACKET)) {
if (match(TokenType.DOT)) {
final String fieldName = consume(TokenType.WORD).getText();
final Expression key = new ValueExpression(fieldName);
indices.add(key);
}
if (match(TokenType.LBRACKET)) {
indices.add(expression());
consume(TokenType.RBRACKET);
}
}
return indices;
} }
private Expression value() { private Expression value() {

View File

@ -1,41 +0,0 @@
package com.annimon.ownlang.parser.ast;
import com.annimon.ownlang.lib.Types;
import com.annimon.ownlang.lib.Value;
import com.annimon.ownlang.lib.Variables;
/**
*
* @author aNNiMON
*/
public final class ArrayAssignmentStatement implements Statement {
public final ArrayAccessExpression array;
public final Expression expression;
public ArrayAssignmentStatement(ArrayAccessExpression array, Expression expression) {
this.array = array;
this.expression = expression;
}
@Override
public void execute() {
final Value container = Variables.get(array.variable);
if (container.type() == Types.ARRAY) {
final int lastIndex = (int) array.lastIndex().asNumber();
array.getArray().set(lastIndex, expression.eval());
return;
}
array.getMap().set(array.lastIndex(), expression.eval());
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@Override
public String toString() {
return String.format("%s = %s", array, expression);
}
}

View File

@ -8,43 +8,53 @@ import java.util.List;
* *
* @author aNNiMON * @author aNNiMON
*/ */
public final class ArrayAccessExpression implements Expression { public final class ContainerAccessExpression implements Expression {
public final String variable; public final String variable;
public final List<Expression> indices; public final List<Expression> indices;
public ArrayAccessExpression(String variable, List<Expression> indices) { public ContainerAccessExpression(String variable, List<Expression> indices) {
this.variable = variable; this.variable = variable;
this.indices = indices; this.indices = indices;
} }
@Override @Override
public Value eval() { public Value eval() {
final Value container = getContainer();
final Value lastIndex = lastIndex();
switch (container.type()) {
case Types.ARRAY:
final int arrayIndex = (int) lastIndex.asNumber();
return ((ArrayValue) container).get(arrayIndex);
case Types.MAP:
return ((MapValue) container).get(lastIndex);
default:
throw new TypeException("Array or map expected");
}
}
public Value getContainer() {
Value container = Variables.get(variable); Value container = Variables.get(variable);
if (container.type() == Types.ARRAY) {
final int lastIndex = (int) lastIndex().asNumber();
return getArray().get(lastIndex);
}
return getMap().get(lastIndex());
}
public ArrayValue getArray() {
ArrayValue array = consumeArray(Variables.get(variable));
final int last = indices.size() - 1; final int last = indices.size() - 1;
for (int i = 0; i < last; i++) { for (int i = 0; i < last; i++) {
final int index = (int) index(i).asNumber(); final Value index = index(i);
array = consumeArray( array.get(index) ); switch (container.type()) {
} case Types.ARRAY:
return array; final int arrayIndex = (int) index.asNumber();
} container = ((ArrayValue) container).get(arrayIndex);
break;
public MapValue getMap() { case Types.MAP:
MapValue map = consumeMap(Variables.get(variable)); container = ((MapValue) container).get(index);
final int last = indices.size() - 1; break;
for (int i = 0; i < last; i++) {
map = consumeMap( map.get(index(i)) ); default:
throw new TypeException("Array or map expected");
}
} }
return map; return container;
} }
public Value lastIndex() { public Value lastIndex() {

View File

@ -0,0 +1,51 @@
package com.annimon.ownlang.parser.ast;
import com.annimon.ownlang.exceptions.TypeException;
import com.annimon.ownlang.lib.ArrayValue;
import com.annimon.ownlang.lib.MapValue;
import com.annimon.ownlang.lib.Types;
import com.annimon.ownlang.lib.Value;
/**
*
* @author aNNiMON
*/
public final class ContainerAssignmentStatement implements Statement {
public final ContainerAccessExpression containerExpr;
public final Expression expression;
public ContainerAssignmentStatement(ContainerAccessExpression array, Expression expression) {
this.containerExpr = array;
this.expression = expression;
}
@Override
public void execute() {
final Value container = containerExpr.getContainer();
final Value lastIndex = containerExpr.lastIndex();
switch (container.type()) {
case Types.ARRAY:
final int arrayIndex = (int) lastIndex.asNumber();
((ArrayValue) container).set(arrayIndex, expression.eval());
return;
case Types.MAP:
((MapValue) container).set(lastIndex, expression.eval());
return;
default:
throw new TypeException("Array or map expected. Got " + container.type());
}
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
@Override
public String toString() {
return String.format("%s = %s", containerExpr, expression);
}
}

View File

@ -6,14 +6,14 @@ package com.annimon.ownlang.parser.ast;
*/ */
public interface Visitor { public interface Visitor {
void visit(ArrayAccessExpression s);
void visit(ArrayAssignmentStatement s);
void visit(ArrayExpression s); void visit(ArrayExpression s);
void visit(AssignmentStatement s); void visit(AssignmentStatement s);
void visit(BinaryExpression s); void visit(BinaryExpression s);
void visit(BlockStatement s); void visit(BlockStatement s);
void visit(BreakStatement s); void visit(BreakStatement s);
void visit(ConditionalExpression s); void visit(ConditionalExpression s);
void visit(ContainerAccessExpression s);
void visit(ContainerAssignmentStatement s);
void visit(ContinueStatement s); void visit(ContinueStatement s);
void visit(DoWhileStatement s); void visit(DoWhileStatement s);
void visit(DestructuringAssignmentStatement s); void visit(DestructuringAssignmentStatement s);

View File

@ -10,19 +10,6 @@ import java.util.Map;
*/ */
public abstract class AbstractVisitor implements Visitor { public abstract class AbstractVisitor implements Visitor {
@Override
public void visit(ArrayAccessExpression s) {
for (Expression index : s.indices) {
index.accept(this);
}
}
@Override
public void visit(ArrayAssignmentStatement s) {
s.array.accept(this);
s.expression.accept(this);
}
@Override @Override
public void visit(ArrayExpression s) { public void visit(ArrayExpression s) {
for (Expression index : s.elements) { for (Expression index : s.elements) {
@ -58,6 +45,19 @@ public abstract class AbstractVisitor implements Visitor {
s.expr2.accept(this); s.expr2.accept(this);
} }
@Override
public void visit(ContainerAccessExpression s) {
for (Expression index : s.indices) {
index.accept(this);
}
}
@Override
public void visit(ContainerAssignmentStatement s) {
s.containerExpr.accept(this);
s.expression.accept(this);
}
@Override @Override
public void visit(ContinueStatement s) { public void visit(ContinueStatement s) {
} }

View File

@ -9,13 +9,13 @@ import com.annimon.ownlang.parser.ast.*;
public final class VariablePrinter extends AbstractVisitor { public final class VariablePrinter extends AbstractVisitor {
@Override @Override
public void visit(ArrayAccessExpression s) { public void visit(AssignmentStatement s) {
super.visit(s); super.visit(s);
System.out.println(s.variable); System.out.println(s.variable);
} }
@Override @Override
public void visit(AssignmentStatement s) { public void visit(ContainerAccessExpression s) {
super.visit(s); super.visit(s);
System.out.println(s.variable); System.out.println(s.variable);
} }