Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[set] Implements syntax & typing #25

Merged
merged 1 commit into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/main/java/leekscript/common/SetType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package leekscript.common;

public class SetType extends Type {

private Type type;

public SetType(Type type) {
super(type == Type.VOID ? "Set<empty>" : type == Type.ANY ? "Set" : "Set<" + type.toString() + ">", "h", "SetLeekValue", "SetLeekValue", "new SetLeekValue()");
this.type = type;
}

@Override
public CastType accepts(Type type) {
if (type instanceof SetType at) {
if (at.type == Type.VOID) return CastType.UPCAST;
if (this.type == Type.VOID) return CastType.UPCAST;
var cast = this.type.accepts(at.type);
if (cast.ordinal() >= CastType.SAFE_DOWNCAST.ordinal()) return CastType.UNSAFE_DOWNCAST;
return cast;
}
return super.accepts(type);
}

@Override
public Type element() {
return type;
}

public boolean isSet() {
return true;
}

public boolean canBeIterable() {
return true;
}

public boolean isIterable() {
return true;
}

@Override
public int hashCode() {
return this.type.hashCode() * 31 + 1;
}

@Override
public boolean equals(Object object) {
if (object instanceof SetType at) {
return this.type.equals(at.type);
}
return false;
}
}
5 changes: 5 additions & 0 deletions src/main/java/leekscript/common/Type.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class Type {
public static final Type VOID = new Type("void", "v", "Object", "Object", "null");
// public static final Type LEGACY_ARRAY = new LegacyArrayType();
public static final Type ARRAY = array(Type.ANY);
public static final Type SET = set(Type.ANY);
public static final Type INTERVAL = new Type("interval", "t", "IntervalLeekValue", "IntervalLeekValue", "new IntervalLeekValue()");
public static final Type ARRAY_INT = array(Type.INT);
public static final Type ARRAY_REAL = array(Type.REAL);
Expand Down Expand Up @@ -242,6 +243,10 @@ public static Type map(Type key, Type value) {
return map;
}

public static Type set(Type type) {
return new SetType(type);
}

public Type key() {
if (this == ANY) {
return Type.ANY;
Expand Down
46 changes: 37 additions & 9 deletions src/main/java/leekscript/compiler/WordCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import leekscript.compiler.expression.LeekObject;
import leekscript.compiler.expression.LeekParameterType;
import leekscript.compiler.expression.LeekParenthesis;
import leekscript.compiler.expression.LeekSet;
import leekscript.compiler.expression.LeekString;
import leekscript.compiler.expression.LeekType;
import leekscript.compiler.expression.LeekVariable;
Expand Down Expand Up @@ -547,24 +548,26 @@ private LeekType eatPrimaryType(boolean first, boolean mandatory) throws LeekCom
if (word.equals("string")) return new LeekType(mTokens.eat(), Type.STRING);
if (word.equals("Class")) return new LeekType(mTokens.eat(), Type.CLASS);
if (word.equals("Object")) return new LeekType(mTokens.eat(), Type.OBJECT);
if (word.equals("Array")) {
if (word.equals("Array") || word.equals("Set")) {
boolean isArray = word.equals("Array");

var array = mTokens.eat();
LeekType arrayType;
LeekType arrayOrSetType;
if (mTokens.get().getType() == TokenType.OPERATOR && mTokens.get().getWord().equals("<")) {
arrayType = new LeekParameterType(array, mTokens.eat());
arrayOrSetType = new LeekParameterType(array, mTokens.eat());
var value = eatType(false, true);
Type valueType = Type.ANY;
if (value != null) valueType = value.getType();

if (!mTokens.get().getWord().startsWith(">")) {
addError(new AnalyzeError(mTokens.get(), AnalyzeErrorLevel.ERROR, Error.CLOSING_CHEVRON_EXPECTED));
}
((LeekParameterType) arrayType).close(mTokens.eat());
arrayType.setType(Type.array(valueType));
((LeekParameterType) arrayOrSetType).close(mTokens.eat());
arrayOrSetType.setType(isArray ? Type.array(valueType) : Type.set(valueType));
} else {
arrayType = new LeekType(array, Type.ARRAY);
arrayOrSetType = new LeekType(array, isArray ? Type.ARRAY : Type.SET);
}
return arrayType;
return arrayOrSetType;
}
if (word.equals("Map")) {
var map = mTokens.eat();
Expand Down Expand Up @@ -1211,10 +1214,14 @@ public ClassMethodBlock classMethod(ClassDeclarationInstruction classDeclaration
}

public Expression readExpression() throws LeekCompilerException {
return readExpression(false);
return readExpression(false, false);
}

public Expression readExpression(boolean inList) throws LeekCompilerException {
return readExpression(inList, false);
}

public Expression readExpression(boolean inList, boolean inSet) throws LeekCompilerException {

var retour = new LeekExpression();

Expand Down Expand Up @@ -1429,7 +1436,7 @@ public Expression readExpression(boolean inList) throws LeekCompilerException {
mTokens.unskip();
}

} else if (word.getType() == TokenType.OPERATOR) {
} else if (word.getType() == TokenType.OPERATOR && (!word.getWord().equals(">") || !inSet)) {

int operator = Operators.getOperator(word.getWord(), getVersion());

Expand Down Expand Up @@ -1520,6 +1527,9 @@ public Expression readExpression(boolean inList) throws LeekCompilerException {
} else if (word.getType() == TokenType.BRACKET_LEFT) {
retour.addExpression(readArrayOrMapOrInterval(mTokens.eat()));

} else if (getVersion() >= 4 && word.getType() == TokenType.OPERATOR && word.getWord().equals("<")) {
retour.addExpression(readSet(mTokens.eat()));

} else if (getVersion() >= 2 && word.getType() == TokenType.ACCOLADE_LEFT) {

// Déclaration d'un objet
Expand Down Expand Up @@ -1641,6 +1651,24 @@ private boolean wordEquals(Token word, String expected) {
return word.getWord().equals(expected);
}

private Expression readSet(Token openingToken) throws LeekCompilerException {
var set = new LeekSet(openingToken);

while (mTokens.get().getType() != TokenType.OPERATOR || !mTokens.get().getWord().equals(">")) {
if (isInterrupted()) throw new LeekCompilerException(mTokens.get(), Error.AI_TIMEOUT);

set.addValue(readExpression(true, true));

if (mTokens.get().getType() == TokenType.VIRG) {
mTokens.skip();
}
}

set.setClosingToken(mTokens.get());

return set;
}

private Expression readArrayOrMapOrInterval(Token openingBracket) throws LeekCompilerException {
if (version < 4) {
return readLegacyArray(openingBracket);
Expand Down
1 change: 1 addition & 0 deletions src/main/java/leekscript/compiler/bloc/MainLeekBlock.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public MainLeekBlock(IACompiler compiler, WordCompiler wordCompiler, AIFile ai)
if (ai.getVersion() >= 4) {
addClass(new ClassDeclarationInstruction(new Token("Map"), 0, ai, true, this, Type.MAP));
addClass(new ClassDeclarationInstruction(new Token("Interval"), 0, ai, true, this, Type.INTERVAL));
addClass(new ClassDeclarationInstruction(new Token("Set"), 0, ai, true, this, Type.SET));
}
addClass(new ClassDeclarationInstruction(new Token("String"), 0, ai, true, this, Type.STRING));
var objectClass = new ClassDeclarationInstruction(new Token("Object"), 0, ai, true, this, Type.OBJECT);
Expand Down
103 changes: 103 additions & 0 deletions src/main/java/leekscript/compiler/expression/LeekSet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package leekscript.compiler.expression;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.stream.Collectors;

import leekscript.common.Type;
import leekscript.compiler.Hover;
import leekscript.compiler.Token;
import leekscript.compiler.JavaWriter;
import leekscript.compiler.Location;
import leekscript.compiler.WordCompiler;
import leekscript.compiler.bloc.MainLeekBlock;
import leekscript.compiler.exceptions.LeekCompilerException;

public class LeekSet extends Expression {

private final ArrayList<Expression> mValues = new ArrayList<Expression>();
private Token openingToken;
private Token closingToken;

public Type type = Type.SET;

public LeekSet(Token openingToken) {
this.openingToken = openingToken;
}

public void addValue(Expression param) {
mValues.add(param);
}

public void setClosingToken(Token closingToken) {
this.closingToken = closingToken;
closingToken.setExpression(this);
openingToken.setExpression(this);
}

@Override
public int getNature() {
return ARRAY;
}

@Override
public Type getType() {
return type;
}

@Override
public String toString() {
return mValues.stream().map(value -> value.toString()).collect(Collectors.joining(", ", "<", ">"));
}

@Override
public boolean validExpression(WordCompiler compiler, MainLeekBlock mainblock) throws LeekExpressionException {
for (Expression parameter : mValues) {
parameter.validExpression(compiler, mainblock);
}
return true;
}

@Override
public void preAnalyze(WordCompiler compiler) throws LeekCompilerException {
for (var value : mValues) {
value.preAnalyze(compiler);
}
}

@Override
public void analyze(WordCompiler compiler) throws LeekCompilerException {
operations = 0;

var types = new HashSet<Type>();
for (var value : mValues) {
value.analyze(compiler);
operations += 2 + value.getOperations();
types.add(value.getType());
}

this.type = Type.set(types.size() == 0 ? Type.VOID : Type.compound(types));
}

@Override
public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) {
writer.addCode("new SetLeekValue(" + writer.getAIThis() + ", new Object[] { ");
for (int i = 0; i < mValues.size(); i++) {
if (i != 0) writer.addCode(", ");
mValues.get(i).writeJavaCode(mainblock, writer);
}
writer.addCode(" })");
}

@Override
public Location getLocation() {
return new Location(openingToken.getLocation(), closingToken.getLocation());
}

@Override
public Hover hover(Token token) {
var hover = new Hover(getType(), getLocation(), toString());
hover.setSize(mValues.size());
return hover;
}
}
6 changes: 6 additions & 0 deletions src/main/java/leekscript/runner/AI.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import leekscript.runner.values.GenericArrayLeekValue;
import leekscript.runner.values.GenericMapLeekValue;
import leekscript.runner.values.IntervalLeekValue;
import leekscript.runner.values.SetLeekValue;
import leekscript.runner.values.LeekValue;
import leekscript.runner.values.ObjectLeekValue;
import leekscript.runner.values.Box;
Expand Down Expand Up @@ -89,6 +90,7 @@ public abstract class AI {
public final ClassLeekValue legacyArrayClass;
public final ClassLeekValue mapClass;
public final ClassLeekValue intervalClass;
public final ClassLeekValue setClass;
public final ClassLeekValue stringClass;
public final ClassLeekValue objectClass;
public final ClassLeekValue functionClass;
Expand Down Expand Up @@ -270,6 +272,7 @@ public double getDouble() {
legacyArrayClass = new ClassLeekValue(this, "Array", valueClass);
mapClass = new ClassLeekValue(this, "Map", valueClass);
intervalClass = new ClassLeekValue(this, "Interval", valueClass);
setClass = new ClassLeekValue(this, "Set", valueClass);
stringClass = new ClassLeekValue(this, "String", valueClass);
objectClass = new ClassLeekValue(this, "Object", valueClass);
functionClass = new ClassLeekValue(this, "Function", valueClass);
Expand Down Expand Up @@ -1448,6 +1451,8 @@ public String export(Object value, Set<Object> visited) throws LeekRunException
return ((ArrayLeekValue) value).getString(this, visited);
} else if (value instanceof MapLeekValue) {
return ((MapLeekValue) value).getString(this, visited);
} else if (value instanceof SetLeekValue) {
return ((SetLeekValue) value).getString(this, visited);
} else if (value instanceof IntervalLeekValue) {
return ((IntervalLeekValue) value).getString(this, visited);
} else if (value instanceof String) {
Expand Down Expand Up @@ -2985,6 +2990,7 @@ public ClassLeekValue classOf(Object value) {
if (value instanceof ArrayLeekValue) return arrayClass;
if (value instanceof MapLeekValue) return mapClass;
if (value instanceof IntervalLeekValue) return intervalClass;
if (value instanceof SetLeekValue) return setClass;
if (value instanceof String) return stringClass;
if (value instanceof ObjectLeekValue) return ((ObjectLeekValue) value).clazz;
if (value instanceof NativeObjectLeekValue)
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/leekscript/runner/values/ClassLeekValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,9 @@ public Object run(AI ai, Object thiz, Object... arguments) throws LeekRunExcepti
if (this == ai.mapClass) {
return new MapLeekValue(ai);
}
if (this == ai.setClass) {
return new SetLeekValue(ai);
}
if (this == ai.objectClass) return new ObjectLeekValue(ai, ai.objectClass);

// Create the actual object
Expand Down
Loading