mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 00:34:20 +03:00
Fix this in non-static class methods
This commit is contained in:
parent
34490813a6
commit
3eddbbcdef
@ -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
|
||||
|
@ -1,5 +1,12 @@
|
||||
# История изменений
|
||||
|
||||
## Next
|
||||
|
||||
### Исправления
|
||||
- Исправлена передача аргументов командной строки скриптам
|
||||
- Исправлен `this` в нестатических методах классов
|
||||
|
||||
|
||||
## 2.0.0
|
||||
|
||||
### Критические изменения
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
try (final var ignored = ScopeHandler.closeableScope()) {
|
||||
if (classInstance != null) {
|
||||
// non-static method
|
||||
ScopeHandler.defineVariableInCurrentScope("this", classInstance.getThisMap());
|
||||
|
||||
try {
|
||||
}
|
||||
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 + ']';
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
44
ownlang-parser/src/test/resources/other/classScope.own
Normal file
44
ownlang-parser/src/test/resources/other/classScope.own
Normal 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 + "}"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user