Skip to content

Commit

Permalink
[set] Implements syntax & typing
Browse files Browse the repository at this point in the history
  • Loading branch information
Hazurl authored and 5pilow committed Aug 31, 2023
1 parent 9f621da commit b53ce68
Show file tree
Hide file tree
Showing 10 changed files with 298 additions and 9 deletions.
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 @@ -33,6 +33,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 @@ -237,6 +238,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 @@ -93,6 +93,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 @@ -2997,6 +3002,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

0 comments on commit b53ce68

Please sign in to comment.