Fix this in non-static class methods

This commit is contained in:
aNNiMON 2024-08-10 23:51:19 +03:00
parent 34490813a6
commit 3eddbbcdef
7 changed files with 97 additions and 59 deletions

View File

@ -3,7 +3,8 @@
## Next
### Fixes
- Fix passing arguments
- Fix passing command-line arguments to scripts
- Fix `this` in non-static class methods
## 2.0.0

View File

@ -1,5 +1,12 @@
# История изменений
## Next
### Исправления
- Исправлена передача аргументов командной строки скриптам
- Исправлен `this` в нестатических методах классов
## 2.0.0
### Критические изменения

View File

@ -5,7 +5,8 @@ import java.util.List;
public record ClassDeclaration(
String name,
List<ClassField> classFields,
List<ClassMethod> classMethods) implements Instantiable {
List<ClassMethod> classMethods
) implements Instantiable {
/**
* Create an instance and put evaluated fields with method declarations

View File

@ -25,9 +25,9 @@ public class ClassInstance implements Value {
thisMap.set(f.name(), f.evaluableValue().eval());
}
public void addMethod(ClassMethod method) {
method.setClassInstance(this);
final String name = method.getName();
public void addMethod(ClassMethod m) {
final String name = m.name();
final var method = new ClassMethod(m, this);
thisMap.set(name, method);
if (name.equals(className)) {
constructor = method;

View File

@ -2,33 +2,28 @@ package com.annimon.ownlang.lib;
import java.util.Objects;
public class ClassMethod implements Function {
private final String name;
private final Function function;
private ClassInstance classInstance;
public record ClassMethod(
String name,
Function function,
ClassInstance classInstance
) implements Function {
public ClassMethod(String name, Function function) {
this.name = name;
this.function = function;
this(name, function, null);
}
public String getName() {
return name;
}
public void setClassInstance(ClassInstance classInstance) {
this.classInstance = classInstance;
public ClassMethod(ClassMethod m, ClassInstance instance) {
this(m.name, m.function, instance);
}
@Override
public Value execute(Value... args) {
ScopeHandler.push();
ScopeHandler.defineVariableInCurrentScope("this", classInstance.getThisMap());
try {
try (final var ignored = ScopeHandler.closeableScope()) {
if (classInstance != null) {
// non-static method
ScopeHandler.defineVariableInCurrentScope("this", classInstance.getThisMap());
}
return function.execute(args);
} finally {
ScopeHandler.pop();
}
}
@ -38,12 +33,11 @@ public class ClassMethod implements Function {
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (ClassMethod) obj;
return Objects.equals(this.name, that.name) &&
Objects.equals(this.function, that.function);
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ClassMethod that)) return false;
return Objects.equals(name, that.name)
&& Objects.equals(function, that.function);
}
@Override
@ -53,8 +47,6 @@ public class ClassMethod implements Function {
@Override
public String toString() {
return "ClassMethod[" +
"name=" + name + ", " +
"function=" + function + ']';
return "ClassMethod[" + name + ']';
}
}

View File

@ -27,7 +27,7 @@ public final class ConditionalExpression implements Node {
private final String name;
private Operator(String name) {
Operator(String name) {
this.name = name;
}
@ -36,7 +36,8 @@ public final class ConditionalExpression implements Node {
}
}
public final Node expr1, expr2;
public final Node expr1;
public final Node expr2;
public final Operator operation;
public ConditionalExpression(Operator operation, Node expr1, Node expr2) {
@ -47,25 +48,20 @@ public final class ConditionalExpression implements Node {
@Override
public Value eval() {
switch (operation) {
case AND:
return NumberValue.fromBoolean((expr1AsInt() != 0) && (expr2AsInt() != 0));
case OR:
return NumberValue.fromBoolean((expr1AsInt() != 0) || (expr2AsInt() != 0));
case NULL_COALESCE:
return nullCoalesce();
default:
return NumberValue.fromBoolean(evalAndCompare());
}
return switch (operation) {
case AND -> NumberValue.fromBoolean((expr1AsInt() != 0) && (expr2AsInt() != 0));
case OR -> NumberValue.fromBoolean((expr1AsInt() != 0) || (expr2AsInt() != 0));
case NULL_COALESCE -> nullCoalesce();
default -> NumberValue.fromBoolean(evalAndCompare());
};
}
private boolean evalAndCompare() {
final Value value1 = expr1.eval();
final Value value2 = expr2.eval();
double number1, number2;
double number1;
double number2;
if (value1.type() == Types.NUMBER) {
number1 = value1.asNumber();
number2 = value2.asNumber();
@ -74,18 +70,15 @@ public final class ConditionalExpression implements Node {
number2 = 0;
}
switch (operation) {
case EQUALS: return number1 == number2;
case NOT_EQUALS: return number1 != number2;
case LT: return number1 < number2;
case LTEQ: return number1 <= number2;
case GT: return number1 > number2;
case GTEQ: return number1 >= number2;
default:
throw new OperationIsNotSupportedException(operation);
}
return switch (operation) {
case EQUALS -> number1 == number2;
case NOT_EQUALS -> number1 != number2;
case LT -> number1 < number2;
case LTEQ -> number1 <= number2;
case GT -> number1 > number2;
case GTEQ -> number1 >= number2;
default -> throw new OperationIsNotSupportedException(operation);
};
}
private Value nullCoalesce() {

View File

@ -0,0 +1,44 @@
use std, debug
def testThisOnSingleInstance() {
s = new ClassScope({"id": 1})
assertEquals(1, s.getId())
assertEquals(1, s.getDataId())
}
def testThisOnMultipleInstances() {
s1 = new ClassScope({"id": 1})
s2 = new ClassScope({"id": 2})
s3 = new ClassScope({"id": 3})
assertEquals(1, s1.getId())
assertEquals(1, s1.getDataId())
assertEquals(2, s2.getId())
assertEquals(2, s2.getDataId())
assertEquals(3, s3.getId())
assertEquals(3, s3.getDataId())
}
def testToString() {
s1 = new ClassScope({"id": 1})
s2 = new ClassScope({"id": 2})
assertEquals("ClassScope{id=1}", s1.toString())
assertEquals("ClassScope{id=2}", s2.toString())
}
class ClassScope {
def ClassScope(data) {
this.id = data.id
this.data = data
}
def getId() {
return this.id
}
def getDataId() {
return this.data.id
}
def toString() {
return "ClassScope{id=" + this.id + "}"
}
}