mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 00:34:20 +03:00
Improve errors displaying for container expressions
This commit is contained in:
parent
d223bbbd0b
commit
f1772746cc
@ -9,9 +9,9 @@ final class std_range implements Function {
|
||||
public Value execute(Value[] args) {
|
||||
Arguments.checkRange(1, 3, args.length);
|
||||
return switch (args.length) {
|
||||
default -> RangeValue.of(0, getLong(args[0]), 1);
|
||||
case 2 -> RangeValue.of(getLong(args[0]), getLong(args[1]), 1);
|
||||
case 3 -> RangeValue.of(getLong(args[0]), getLong(args[1]), getLong(args[2]));
|
||||
default -> RangeValue.of(0, getLong(args[0]), 1);
|
||||
};
|
||||
}
|
||||
|
||||
@ -41,9 +41,13 @@ final class std_range implements Function {
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.step = step;
|
||||
final long base = (from < to) ? (to - from) : (from - to);
|
||||
|
||||
final long base = (from < to)
|
||||
? Math.subtractExact(to, from)
|
||||
: Math.subtractExact(from, to);
|
||||
final long absStep = (step < 0) ? -step : step;
|
||||
this.size = (int) (base / absStep + (base % absStep == 0 ? 0 : 1));
|
||||
final long longSize = (base / absStep + (base % absStep == 0 ? 0 : 1));
|
||||
this.size = longSize > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) longSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -67,9 +71,10 @@ final class std_range implements Function {
|
||||
private boolean isIntegerRange() {
|
||||
if (to > 0) {
|
||||
return (from > Integer.MIN_VALUE && to < Integer.MAX_VALUE);
|
||||
}
|
||||
} else {
|
||||
return (to > Integer.MIN_VALUE && from < Integer.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
@ -78,10 +83,12 @@ final class std_range implements Function {
|
||||
|
||||
@Override
|
||||
public Value get(int index) {
|
||||
long value = from + index * step;
|
||||
if (isIntegerRange()) {
|
||||
return NumberValue.of((int) (from + index * step));
|
||||
return NumberValue.of((int) (value));
|
||||
} else {
|
||||
return NumberValue.of(value);
|
||||
}
|
||||
return NumberValue.of(from + (long) index * step);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,8 +1,14 @@
|
||||
package com.annimon.ownlang.exceptions;
|
||||
|
||||
import com.annimon.ownlang.util.Range;
|
||||
|
||||
public final class TypeException extends OwnLangRuntimeException {
|
||||
|
||||
public TypeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public TypeException(String message, Range range) {
|
||||
super(message, range);
|
||||
}
|
||||
}
|
||||
|
@ -353,10 +353,10 @@ public final class Parser {
|
||||
|
||||
if (lookMatch(0, TokenType.LPAREN)) {
|
||||
// next function call
|
||||
return functionChain(new ContainerAccessExpression(expr, indices));
|
||||
return functionChain(new ContainerAccessExpression(expr, indices, getRange()));
|
||||
}
|
||||
// container access
|
||||
return new ContainerAccessExpression(expr, indices);
|
||||
return new ContainerAccessExpression(expr, indices, getRange());
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
@ -532,7 +532,7 @@ public final class Parser {
|
||||
final BinaryExpression.Operator op = ASSIGN_OPERATORS.get(currentType);
|
||||
final Node expression = expression();
|
||||
|
||||
return new AssignmentExpression(op, (Accessible) targetExpr, expression);
|
||||
return new AssignmentExpression(op, (Accessible) targetExpr, expression, getRange(position, index));
|
||||
}
|
||||
|
||||
private Node ternary() {
|
||||
@ -765,9 +765,7 @@ public final class Parser {
|
||||
args.add(expression());
|
||||
match(TokenType.COMMA);
|
||||
}
|
||||
final var expr = new ObjectCreationExpression(className, args);
|
||||
expr.setRange(getRange(startTokenIndex, index - 1));
|
||||
return expr;
|
||||
return new ObjectCreationExpression(className, args, getRange(startTokenIndex, index - 1));
|
||||
}
|
||||
|
||||
return unary();
|
||||
@ -859,12 +857,13 @@ public final class Parser {
|
||||
if (!match(TokenType.WORD)) return null;
|
||||
|
||||
final List<Node> indices = variableSuffix();
|
||||
if (indices == null || indices.isEmpty()) {
|
||||
final var variable = new VariableExpression(current.text());
|
||||
variable.setRange(getRange(startTokenIndex, index - 1));
|
||||
if (indices == null || indices.isEmpty()) {
|
||||
return variable;
|
||||
} else {
|
||||
return new ContainerAccessExpression(variable, indices, variable.getRange());
|
||||
}
|
||||
return new ContainerAccessExpression(current.text(), indices);
|
||||
}
|
||||
|
||||
private List<Node> variableSuffix() {
|
||||
@ -905,15 +904,16 @@ public final class Parser {
|
||||
if (lookMatch(1, TokenType.WORD) && lookMatch(2, TokenType.LPAREN)) {
|
||||
match(TokenType.DOT);
|
||||
return functionChain(new ContainerAccessExpression(
|
||||
strExpr, Collections.singletonList(
|
||||
new ValueExpression(consume(TokenType.WORD).text())
|
||||
)));
|
||||
strExpr,
|
||||
Collections.singletonList(new ValueExpression(consume(TokenType.WORD).text())),
|
||||
getRange()
|
||||
));
|
||||
}
|
||||
final List<Node> indices = variableSuffix();
|
||||
if (indices == null || indices.isEmpty()) {
|
||||
return strExpr;
|
||||
}
|
||||
return new ContainerAccessExpression(strExpr, indices);
|
||||
return new ContainerAccessExpression(strExpr, indices, getRange());
|
||||
}
|
||||
return strExpr;
|
||||
}
|
||||
|
@ -1,22 +1,32 @@
|
||||
package com.annimon.ownlang.parser.ast;
|
||||
|
||||
import com.annimon.ownlang.exceptions.OwnLangRuntimeException;
|
||||
import com.annimon.ownlang.lib.EvaluableValue;
|
||||
import com.annimon.ownlang.lib.Value;
|
||||
import com.annimon.ownlang.util.Range;
|
||||
import com.annimon.ownlang.util.SourceLocation;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author aNNiMON
|
||||
*/
|
||||
public final class AssignmentExpression extends InterruptableNode implements Statement, EvaluableValue {
|
||||
public final class AssignmentExpression extends InterruptableNode implements Statement, EvaluableValue, SourceLocation {
|
||||
|
||||
public final Accessible target;
|
||||
public final BinaryExpression.Operator operation;
|
||||
public final Node expression;
|
||||
private final Range range;
|
||||
|
||||
public AssignmentExpression(BinaryExpression.Operator operation, Accessible target, Node expr) {
|
||||
public AssignmentExpression(BinaryExpression.Operator operation, Accessible target, Node expr, Range range) {
|
||||
this.operation = operation;
|
||||
this.target = target;
|
||||
this.expression = expr;
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range getRange() {
|
||||
return range;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -24,11 +34,19 @@ public final class AssignmentExpression extends InterruptableNode implements Sta
|
||||
super.interruptionCheck();
|
||||
if (operation == null) {
|
||||
// Simple assignment
|
||||
return target.set(expression.eval());
|
||||
return target.set(checkNonNull(expression.eval(), "Assignment expression"));
|
||||
}
|
||||
final Node expr1 = new ValueExpression(target.get());
|
||||
final Node expr2 = new ValueExpression(expression.eval());
|
||||
return target.set(new BinaryExpression(operation, expr1, expr2).eval());
|
||||
final Node expr1 = new ValueExpression(checkNonNull(target.get(), "Assignment target"));
|
||||
final Node expr2 = new ValueExpression(checkNonNull(expression.eval(), "Assignment expression"));
|
||||
final Value result = new BinaryExpression(operation, expr1, expr2).eval();
|
||||
return target.set(result);
|
||||
}
|
||||
|
||||
private Value checkNonNull(Value value, String message) {
|
||||
if (value == null) {
|
||||
throw new OwnLangRuntimeException(message + " evaluates to null", range);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,7 +1,10 @@
|
||||
package com.annimon.ownlang.parser.ast;
|
||||
|
||||
import com.annimon.ownlang.exceptions.OwnLangRuntimeException;
|
||||
import com.annimon.ownlang.exceptions.TypeException;
|
||||
import com.annimon.ownlang.lib.*;
|
||||
import com.annimon.ownlang.util.Range;
|
||||
import com.annimon.ownlang.util.SourceLocation;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -9,7 +12,7 @@ import java.util.regex.Pattern;
|
||||
*
|
||||
* @author aNNiMON
|
||||
*/
|
||||
public final class ContainerAccessExpression implements Node, Accessible {
|
||||
public final class ContainerAccessExpression implements Node, Accessible, SourceLocation {
|
||||
|
||||
private static final Pattern PATTERN_SIMPLE_INDEX = Pattern.compile("^\"[a-zA-Z$_]\\w*\"");
|
||||
|
||||
@ -17,15 +20,13 @@ public final class ContainerAccessExpression implements Node, Accessible {
|
||||
public final List<Node> indices;
|
||||
private final boolean[] simpleIndices;
|
||||
private final boolean rootIsVariable;
|
||||
private final Range range;
|
||||
|
||||
public ContainerAccessExpression(String variable, List<Node> indices) {
|
||||
this(new VariableExpression(variable), indices);
|
||||
}
|
||||
|
||||
public ContainerAccessExpression(Node root, List<Node> indices) {
|
||||
public ContainerAccessExpression(Node root, List<Node> indices, Range range) {
|
||||
rootIsVariable = root instanceof VariableExpression;
|
||||
this.root = root;
|
||||
this.indices = indices;
|
||||
this.range = range;
|
||||
simpleIndices = precomputeSimpleIndices();
|
||||
}
|
||||
|
||||
@ -33,6 +34,11 @@ public final class ContainerAccessExpression implements Node, Accessible {
|
||||
return rootIsVariable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range getRange() {
|
||||
return range;
|
||||
}
|
||||
|
||||
public Node getRoot() {
|
||||
return root;
|
||||
}
|
||||
@ -47,11 +53,24 @@ public final class ContainerAccessExpression implements Node, Accessible {
|
||||
final Value container = getContainer();
|
||||
final Value lastIndex = lastIndex();
|
||||
return switch (container.type()) {
|
||||
case Types.ARRAY -> ((ArrayValue) container).get(lastIndex);
|
||||
case Types.ARRAY -> {
|
||||
final ArrayValue arr = (ArrayValue) container;
|
||||
final int size = arr.size();
|
||||
if (lastIndex.type() != Types.NUMBER) {
|
||||
yield arr.get(lastIndex);
|
||||
} else {
|
||||
final int index = lastIndex.asInt();
|
||||
if (0 <= index && index < size) {
|
||||
yield arr.get(index);
|
||||
} else {
|
||||
throw outOfBounds(index, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
case Types.MAP -> ((MapValue) container).get(lastIndex);
|
||||
case Types.STRING -> ((StringValue) container).access(lastIndex);
|
||||
case Types.CLASS -> ((ClassInstance) container).access(lastIndex);
|
||||
default -> throw new TypeException("Array or map expected. Got " + Types.typeToString(container.type()));
|
||||
default -> throw arrayOrMapExpected(container, " while accessing a container");
|
||||
};
|
||||
}
|
||||
|
||||
@ -60,10 +79,19 @@ public final class ContainerAccessExpression implements Node, Accessible {
|
||||
final Value container = getContainer();
|
||||
final Value lastIndex = lastIndex();
|
||||
switch (container.type()) {
|
||||
case Types.ARRAY -> ((ArrayValue) container).set(lastIndex.asInt(), value);
|
||||
case Types.ARRAY -> {
|
||||
final ArrayValue arr = (ArrayValue) container;
|
||||
final int size = arr.size();
|
||||
final int index = lastIndex.asInt();
|
||||
if (0 <= index && index < size) {
|
||||
arr.set(index, value);
|
||||
} else {
|
||||
throw outOfBounds(index, size);
|
||||
}
|
||||
}
|
||||
case Types.MAP -> ((MapValue) container).set(lastIndex, value);
|
||||
case Types.CLASS -> ((ClassInstance) container).set(lastIndex, value);
|
||||
default -> throw new TypeException("Array or map expected. Got " + container.type());
|
||||
default -> throw arrayOrMapExpected(container, " while setting a value to container");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@ -76,7 +104,7 @@ public final class ContainerAccessExpression implements Node, Accessible {
|
||||
container = switch (container.type()) {
|
||||
case Types.ARRAY -> ((ArrayValue) container).get(index.asInt());
|
||||
case Types.MAP -> ((MapValue) container).get(index);
|
||||
default -> throw new TypeException("Array or map expected");
|
||||
default -> throw arrayOrMapExpected(container, " while resolving a container");
|
||||
};
|
||||
}
|
||||
return container;
|
||||
@ -97,6 +125,17 @@ public final class ContainerAccessExpression implements Node, Accessible {
|
||||
return (MapValue) value;
|
||||
}
|
||||
|
||||
private OwnLangRuntimeException outOfBounds(int index, int size) {
|
||||
return new OwnLangRuntimeException(
|
||||
"Index %d is out of bounds for array length %d".formatted(index, size), range);
|
||||
}
|
||||
|
||||
private TypeException arrayOrMapExpected(Value v, String message) {
|
||||
return new TypeException("Array or map expected"
|
||||
+ (message == null ? "" : message)
|
||||
+ ". Got " + Types.typeToString(v.type()), range);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.visit(this);
|
||||
|
@ -11,14 +11,11 @@ public final class ObjectCreationExpression implements Node, SourceLocation {
|
||||
|
||||
public final String className;
|
||||
public final List<Node> constructorArguments;
|
||||
private Range range;
|
||||
private final Range range;
|
||||
|
||||
public ObjectCreationExpression(String className, List<Node> constructorArguments) {
|
||||
public ObjectCreationExpression(String className, List<Node> constructorArguments, Range range) {
|
||||
this.className = className;
|
||||
this.constructorArguments = constructorArguments;
|
||||
}
|
||||
|
||||
public void setRange(Range range) {
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ public abstract class OptimizationVisitor<T> implements ResultVisitor<Node, T> {
|
||||
final Node exprNode = s.expression.accept(this, t);
|
||||
final Node targetNode = s.target.accept(this, t);
|
||||
if ( (exprNode != s.expression || targetNode != s.target) && (targetNode instanceof Accessible) ) {
|
||||
return new AssignmentExpression(s.operation, (Accessible) targetNode, exprNode);
|
||||
return new AssignmentExpression(s.operation, (Accessible) targetNode, exprNode, s.getRange());
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@ -79,7 +79,7 @@ public abstract class OptimizationVisitor<T> implements ResultVisitor<Node, T> {
|
||||
final AssignmentExpression newField;
|
||||
if (fieldExpr != field.expression) {
|
||||
changed = true;
|
||||
newField = new AssignmentExpression(field.operation, field.target, fieldExpr);
|
||||
newField = new AssignmentExpression(field.operation, field.target, fieldExpr, field.getRange());
|
||||
} else {
|
||||
newField = field;
|
||||
}
|
||||
@ -126,7 +126,7 @@ public abstract class OptimizationVisitor<T> implements ResultVisitor<Node, T> {
|
||||
indices.add(node);
|
||||
}
|
||||
if (changed) {
|
||||
return new ContainerAccessExpression(root, indices);
|
||||
return new ContainerAccessExpression(root, indices, s.getRange());
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@ -356,7 +356,7 @@ public abstract class OptimizationVisitor<T> implements ResultVisitor<Node, T> {
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
return new ObjectCreationExpression(s.className, args);
|
||||
return new ObjectCreationExpression(s.className, args, s.getRange());
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ public final class ASTHelper {
|
||||
}
|
||||
|
||||
public static AssignmentExpression assign(BinaryExpression.Operator op, Accessible accessible, Node expr) {
|
||||
return new AssignmentExpression(op, accessible, expr);
|
||||
return new AssignmentExpression(op, accessible, expr, null);
|
||||
}
|
||||
|
||||
public static BinaryExpression operator(BinaryExpression.Operator op, Node left, Node right) {
|
||||
|
Loading…
Reference in New Issue
Block a user