mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 00:34:20 +03:00
Улучшен доступ к вложенным контейнерам
This commit is contained in:
parent
9536c7f5b0
commit
32d40d9d04
@ -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]
|
||||||
|
@ -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,18 +564,36 @@ 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 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;
|
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() {
|
||||||
final Token current = get(0);
|
final Token current = get(0);
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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() {
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
@ -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) {
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user