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

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
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);
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);
return new ArrayAssignmentStatement(array, expression());
final ContainerAccessExpression containerExpr = (ContainerAccessExpression) qualifiedNameExpr;
return new ContainerAssignmentStatement(containerExpr, expression());
}
throw new ParseException("Unknown statement: " + get(0));
}
@ -249,31 +251,6 @@ public final class Parser {
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() {
// match expression {
// case pattern1: result1
@ -563,9 +540,11 @@ public final class Parser {
}
private Expression variable() {
// function(...
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LPAREN)) {
return function(new ValueExpression(consume(TokenType.WORD).getText()));
}
final Expression qualifiedNameExpr = qualifiedName();
if (qualifiedNameExpr != null) {
// variable(args) || arr["key"](args) || obj.key(args)
@ -585,17 +564,35 @@ public final class Parser {
}
private Expression qualifiedName() {
// var || var.key[index].key2
final Token current = get(0);
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LBRACKET)) {
return element();
}
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.DOT)) {
return object();
}
if (match(TokenType.WORD)) {
if (!match(TokenType.WORD)) return null;
final List<Expression> indices = variableSuffix();
if ((indices == null) || indices.isEmpty()) {
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() {

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
*/
public final class ArrayAccessExpression implements Expression {
public final class ContainerAccessExpression implements Expression {
public final String variable;
public final List<Expression> indices;
public ArrayAccessExpression(String variable, List<Expression> indices) {
public ContainerAccessExpression(String variable, List<Expression> indices) {
this.variable = variable;
this.indices = indices;
}
@Override
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);
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;
for (int i = 0; i < last; i++) {
final int index = (int) index(i).asNumber();
array = consumeArray( array.get(index) );
}
return array;
}
final Value index = index(i);
switch (container.type()) {
case Types.ARRAY:
final int arrayIndex = (int) index.asNumber();
container = ((ArrayValue) container).get(arrayIndex);
break;
public MapValue getMap() {
MapValue map = consumeMap(Variables.get(variable));
final int last = indices.size() - 1;
for (int i = 0; i < last; i++) {
map = consumeMap( map.get(index(i)) );
case Types.MAP:
container = ((MapValue) container).get(index);
break;
default:
throw new TypeException("Array or map expected");
}
}
return map;
return container;
}
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 {
void visit(ArrayAccessExpression s);
void visit(ArrayAssignmentStatement s);
void visit(ArrayExpression s);
void visit(AssignmentStatement s);
void visit(BinaryExpression s);
void visit(BlockStatement s);
void visit(BreakStatement s);
void visit(ConditionalExpression s);
void visit(ContainerAccessExpression s);
void visit(ContainerAssignmentStatement s);
void visit(ContinueStatement s);
void visit(DoWhileStatement s);
void visit(DestructuringAssignmentStatement s);

View File

@ -10,19 +10,6 @@ import java.util.Map;
*/
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
public void visit(ArrayExpression s) {
for (Expression index : s.elements) {
@ -58,6 +45,19 @@ public abstract class AbstractVisitor implements Visitor {
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
public void visit(ContinueStatement s) {
}

View File

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