Skip to content
Open
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
35 changes: 35 additions & 0 deletions src/main/java/leekscript/common/EnumType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package leekscript.common;

import leekscript.compiler.instruction.EnumDeclarationInstruction;

public class EnumType extends Type {

private EnumDeclarationInstruction enumDeclaration;

public EnumType(EnumDeclarationInstruction enumDeclaration) {
super(enumDeclaration.getName(), "e", "Object", "Object", "null");
this.enumDeclaration = enumDeclaration;
}

@Override
public EnumDeclarationInstruction getEnumDeclaration() {
return enumDeclaration;
}

@Override
public CastType accepts(Type type) {
if (type == this) return CastType.EQUALS;
if (this == ANY) return CastType.UPCAST;
if (type == ANY) return CastType.UNSAFE_DOWNCAST;
if (type instanceof EnumType et) {
if (this.enumDeclaration == et.enumDeclaration) return CastType.EQUALS;
return CastType.INCOMPATIBLE;
}
return super.accepts(type);
}

@Override
public String getCode() {
return enumDeclaration.getName();
}
}
64 changes: 64 additions & 0 deletions src/main/java/leekscript/common/EnumValueType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package leekscript.common;

import leekscript.compiler.instruction.EnumDeclarationInstruction;

public class EnumValueType extends Type {

private EnumDeclarationInstruction enumDeclaration;

public EnumValueType(EnumDeclarationInstruction enumDeclaration) {
super("Enum<" + (enumDeclaration == null ? "?" : enumDeclaration.getName()) + ">", "E", "EnumLeekValue", "EnumLeekValue", "null");
this.enumDeclaration = enumDeclaration;
}

public Type member(String member) {
if (member == null) return Type.VOID;
if (enumDeclaration == null) return Type.VOID;
var constant = enumDeclaration.getConstant(member);
if (constant != null) {
return constant.getType();
}
return Type.VOID;
}

@Override
public Type elementAccess(int version, boolean strict, String key) {
return member(key);
}

@Override
public EnumDeclarationInstruction getEnumDeclaration() {
return enumDeclaration;
}

@Override
public CastType accepts(Type type) {
if (type instanceof EnumValueType et) {
// Same concrete enum declaration -> exactly equal
if (this.enumDeclaration == et.enumDeclaration) {
return CastType.EQUALS;
}
// Generic Enum (null declaration) can accept any specific enum as an upcast
if (this.enumDeclaration == null && et.enumDeclaration != null) {
return CastType.UPCAST;
}
// Casting from a generic Enum to a specific enum is a downcast
if (this.enumDeclaration != null && et.enumDeclaration == null) {
return CastType.SAFE_DOWNCAST;
}
// Two different concrete enums are incompatible
return CastType.INCOMPATIBLE;
}
return super.accepts(type);
}

@Override
public boolean isIndexable() {
return false;
}

@Override
public String getCode() {
return enumDeclaration == null ? "Enum" : "Enum<" + enumDeclaration.getName() + ">";
}
}
3 changes: 3 additions & 0 deletions src/main/java/leekscript/common/Error.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,7 @@ public enum Error {
DEFAULT_ARGUMENT_NOT_END, // 145
CASE_OR_DEFAULT_EXPECTED, // 146
COLON_EXPECTED_AFTER_CASE, // 147
INCOMPLETE_ENUM_SWITCH, // 148
ENUM_MEMBER_DOES_NOT_EXIST, // 149
DUPLICATED_ENUM_CONSTANT, // 150
}
4 changes: 4 additions & 0 deletions src/main/java/leekscript/common/Type.java
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,10 @@ public ClassDeclarationInstruction getClassDeclaration() {
return null;
}

public leekscript.compiler.instruction.EnumDeclarationInstruction getEnumDeclaration() {
return null;
}

public static FunctionType add_argument(FunctionType current, Type argument) {
// var entry = new AbstractMap.SimpleEntry<FunctionType, Type>(current, argument);
// var cached = addArgumentTypes.get(entry);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/leekscript/compiler/LexicalParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ private boolean tryParseIdentifier() {
addToken(word, TokenType.CONST);
} else if (version >= 3 && wordEquals(word, "char")) {
addToken(word, TokenType.CHAR);
} else if (version >= 3 && wordEquals(word, "enum")) {
} else if (version >= 4 && wordEquals(word, "enum")) {
addToken(word, TokenType.ENUM);
} else if (version >= 3 && wordEquals(word, "eval")) {
addToken(word, TokenType.EVAL);
Expand Down
85 changes: 84 additions & 1 deletion src/main/java/leekscript/compiler/WordCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import leekscript.compiler.expression.LeekVariable.VariableType;
import leekscript.compiler.instruction.BlankInstruction;
import leekscript.compiler.instruction.ClassDeclarationInstruction;
import leekscript.compiler.instruction.EnumDeclarationInstruction;
import leekscript.compiler.instruction.LeekBreakInstruction;
import leekscript.compiler.instruction.LeekContinueInstruction;
import leekscript.compiler.instruction.LeekExpressionInstruction;
Expand Down Expand Up @@ -218,6 +219,22 @@ public void firstPass() throws LeekCompilerException {
// System.out.println("Define class " + clazz.getName());
}
}
} else if (getVersion() >= 4 && mTokens.get().getType() == TokenType.ENUM) {

mTokens.skip();
if (mTokens.hasMoreTokens()) {
var enumName = mTokens.eat();

if (enumName.getType() == TokenType.STRING) {

if (mMain.getDefinedClass(enumName.getWord()) != null || mMain.getDefinedEnum(enumName.getWord()) != null) {
throw new LeekCompilerException(enumName, Error.VARIABLE_NAME_UNAVAILABLE, new String[] { enumName.getWord() });
}

var enumDecl = new EnumDeclarationInstruction(enumName, mLine, mAI, getMainBlock());
mMain.defineEnum(enumDecl);
}
}
} else {
mTokens.skip();
}
Expand Down Expand Up @@ -359,6 +376,12 @@ private void compileWord() throws LeekCompilerException {
classDeclaration();
return;

} else if (version >= 4 && getCurrentBlock() instanceof MainLeekBlock && word.getType() == TokenType.ENUM) {

mTokens.skip();
enumDeclaration();
return;

} else if (word.getType() == TokenType.BREAK) {

if (!mCurentBlock.isBreakable()) {
Expand Down Expand Up @@ -699,6 +722,11 @@ private LeekType eatPrimaryType(boolean first, boolean mandatory) throws LeekCom
return new LeekType(mTokens.eat(), clazz.getType());
}

var enumDecl = mMain.getDefinedEnum(word);
if (enumDecl != null) {
return new LeekType(mTokens.eat(), enumDecl.getType());
}

if (mandatory) {
addError(new AnalyzeError(mTokens.get(), AnalyzeErrorLevel.ERROR, Error.TYPE_EXPECTED));
}
Expand Down Expand Up @@ -1208,6 +1236,59 @@ public void classDeclaration() throws LeekCompilerException {
mCurrentClass = null;
}

public void enumDeclaration() throws LeekCompilerException {
Token word = mTokens.eat();
if (word.getType() != TokenType.STRING) {
throw new LeekCompilerException(word, Error.VAR_NAME_EXPECTED);
}
if (isKeyword(word)) {
addError(new AnalyzeError(word, AnalyzeErrorLevel.ERROR, Error.VARIABLE_NAME_UNAVAILABLE, new String[] { word.getWord() }));
}
EnumDeclarationInstruction enumDeclaration = mMain.getDefinedEnum(word.getWord());
if (enumDeclaration == null) {
throw new LeekCompilerException(word, Error.UNKNOWN_ERROR);
}
mMain.addEnumList(enumDeclaration);

if (mTokens.get().getType() != TokenType.ACCOLADE_LEFT) {
throw new LeekCompilerException(mTokens.get(), Error.OPENING_CURLY_BRACKET_EXPECTED);
}
mTokens.skip();

while (mTokens.hasMoreTokens() && mTokens.get().getType() != TokenType.ACCOLADE_RIGHT) {
if (isInterrupted()) throw new LeekCompilerException(mTokens.get(), Error.AI_TIMEOUT);
word = mTokens.get();
if (word.getType() == TokenType.STRING) {
Token nameToken = mTokens.eat();

// Duplicate constant name inside the same enum
if (enumDeclaration.getConstant(nameToken.getWord()) != null) {
addError(new AnalyzeError(nameToken, AnalyzeErrorLevel.ERROR, Error.DUPLICATED_ENUM_CONSTANT, new String[] {
enumDeclaration.getName(),
nameToken.getWord()
}));
} else {
Expression value = null;
if (mTokens.hasMoreTokens() && mTokens.get().getType() == TokenType.OPERATOR && mTokens.get().getWord().equals("=")) {
mTokens.skip(); // skip '='
var expr = readExpression();
value = expr;
}
enumDeclaration.addConstant(nameToken, value);
}
}
if (mTokens.hasMoreTokens() && mTokens.get().getType() == TokenType.VIRG) {
mTokens.skip();
}
}
if (mTokens.hasMoreTokens() && mTokens.get().getType() != TokenType.ACCOLADE_RIGHT) {
throw new LeekCompilerException(mTokens.get(), Error.END_OF_CLASS_EXPECTED);
}
if (mTokens.hasMoreTokens()) {
mTokens.skip(); // accolade right
}
}

public void classStaticMember(ClassDeclarationInstruction classDeclaration, AccessLevel accessLevel) throws LeekCompilerException {
Token token = mTokens.get();
switch (token.getWord()) {
Expand Down Expand Up @@ -1585,7 +1666,7 @@ public Expression readExpression(boolean inList, boolean inSet, boolean inInterv
} else if (word.getType() == TokenType.DOT) {
// Object access
var dot = mTokens.eat();
if (mTokens.get().getType() == TokenType.STRING || mTokens.get().getType() == TokenType.CLASS || mTokens.get().getType() == TokenType.SUPER) {
if (mTokens.get().getType() == TokenType.STRING || mTokens.get().getType() == TokenType.CLASS || mTokens.get().getType() == TokenType.ENUM || mTokens.get().getType() == TokenType.SUPER) {
var name = mTokens.get();
retour.addObjectAccess(dot, name);
} else {
Expand Down Expand Up @@ -1765,6 +1846,8 @@ public Expression readExpression(boolean inList, boolean inSet, boolean inInterv

} else if (word.getType() == TokenType.CLASS) {
retour.addExpression(new LeekVariable(this, word, VariableType.LOCAL));
} else if (word.getType() == TokenType.ENUM) {
retour.addExpression(new LeekVariable(this, word, VariableType.LOCAL));
} else if (word.getType() == TokenType.THIS) {
retour.addExpression(new LeekVariable(this, word, VariableType.LOCAL));
} else if (word.getType() == TokenType.TRUE) {
Expand Down
35 changes: 35 additions & 0 deletions src/main/java/leekscript/compiler/bloc/MainLeekBlock.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import leekscript.compiler.expression.LeekNumber;
import leekscript.compiler.expression.LeekVariable.VariableType;
import leekscript.compiler.instruction.ClassDeclarationInstruction;
import leekscript.compiler.instruction.EnumDeclarationInstruction;
import leekscript.compiler.instruction.LeekGlobalDeclarationInstruction;
import leekscript.runner.LeekFunctions;
import leekscript.common.Error;
Expand All @@ -38,6 +39,8 @@ public class MainLeekBlock extends AbstractLeekBlock {
private final Map<String, ClassDeclarationInstruction> mDefinedClasses = new TreeMap<String, ClassDeclarationInstruction>();
private final Map<String, ClassDeclarationInstruction> mUserClasses = new TreeMap<String, ClassDeclarationInstruction>();
private final List<ClassDeclarationInstruction> mUserClassesList = new ArrayList<>();
private final Map<String, EnumDeclarationInstruction> mDefinedEnums = new TreeMap<String, EnumDeclarationInstruction>();
private final List<EnumDeclarationInstruction> mUserEnumsList = new ArrayList<>();
private int mMinLevel = 1;
private int mAnonymousId = 1;
private int mFunctionId = 1;
Expand Down Expand Up @@ -245,6 +248,9 @@ public String getCode() {
if (clazz.internal) continue;
str += clazz.getCode() + "\n";
}
for (var enumDecl : mUserEnumsList) {
str += enumDecl.getCode() + "\n";
}
return str + super.getCode();
}

Expand All @@ -267,6 +273,11 @@ public void writeJavaCode(JavaWriter writer, String className, String AIClass, O
clazz.declareJava(this, writer);
}

// Enums
for (var enumDecl : mUserEnumsList) {
enumDecl.declareJava(this, writer);
}

// Constructor
writer.addLine("public " + className + "() throws LeekRunException {");
writer.addLine("super(" + mInstructions.size() + ", " + mCompiler.getCurrentAI().getVersion() + ");");
Expand All @@ -275,6 +286,9 @@ public void writeJavaCode(JavaWriter writer, String className, String AIClass, O
if (clazz.internal) continue;
clazz.createJava(this, writer);
}
for (var enumDecl : mUserEnumsList) {
enumDecl.createJava(this, writer);
}
writer.addLine("}");

// Classes initialize functions
Expand Down Expand Up @@ -419,10 +433,25 @@ public Map<String, ClassDeclarationInstruction> getDefinedClasses() {
return mDefinedClasses;
}

public void defineEnum(EnumDeclarationInstruction enumDeclaration) {
mDefinedEnums.put(enumDeclaration.getName(), enumDeclaration);
}

public void addEnumList(EnumDeclarationInstruction enumDeclaration) {
mUserEnumsList.add(enumDeclaration);
}

public EnumDeclarationInstruction getDefinedEnum(String name) {
return mDefinedEnums.get(name);
}

public void preAnalyze(WordCompiler compiler) throws LeekCompilerException {
for (var clazz : mUserClassesList) {
clazz.declare(compiler);
}
for (var enumDecl : mUserEnumsList) {
enumDecl.declare(compiler);
}
for (var function : mFunctions.values()) {
function.declare(compiler);
}
Expand All @@ -432,6 +461,9 @@ public void preAnalyze(WordCompiler compiler) throws LeekCompilerException {
for (var clazz : mUserClassesList) {
clazz.preAnalyze(compiler);
}
for (var enumDecl : mUserEnumsList) {
enumDecl.preAnalyze(compiler);
}
for (var function : mFunctions.values()) {
function.preAnalyze(compiler);
}
Expand All @@ -445,6 +477,9 @@ public void analyze(WordCompiler compiler) throws LeekCompilerException {
for (var clazz : mUserClassesList) {
clazz.analyze(compiler);
}
for (var enumDecl : mUserEnumsList) {
enumDecl.analyze(compiler);
}
for (var function : mFunctions.values()) {
function.analyze(compiler);
}
Expand Down
Loading