diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..a397e3af --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,34 @@ +# This workflow will build a Java project with Gradle +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle + +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 16 + uses: actions/setup-java@v2 + with: + java-version: '16' + distribution: 'adopt' + - name: Setup locale + run: | + sudo locale-gen fr_FR.UTF-8 + sudo update-locale + export LANG=fr_FR.utf8 + export JAVA_TOOL_OPTIONS="-Duser.language=fr -Duser.region=FR" + export _JAVA_OPTIONS="-Duser.language=fr -Duser.region=FR" + export JAVA_ARGS="-Duser.language=fr -Duser.region=FR" + locale + - name: Build + run: gradle build diff --git a/.gitignore b/.gitignore index 17da52f3..463c1dc9 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ build/ .project /ai/ /bin/ +.vscode/ \ No newline at end of file diff --git a/README.md b/README.md index 145aa13a..ab081901 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,22 @@ -# LeekScript v1 +# LeekScript -First version of LeekScript language, built in Java. +[![CI](https://github.com/leek-wars/leekscript/actions/workflows/build.yml/badge.svg)](https://github.com/leek-wars/leekscript/actions/workflows/build.yml) +The language of Leek Wars, built in Java. +Used in the [leek-wars-generator](https://github.com/leek-wars/leek-wars-generator) project. + +### Build +``` +gradle jar +``` +### Run a console +``` +java -jar leekscript.jar +``` ### Build and run tests ``` gradle jar test ``` ### Credits -Developed by Dawyde © 2012-2019 +Developed by Dawyde & Pilow © 2012-2022 diff --git a/build.gradle b/build.gradle index de59f128..de0b209d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,20 +1,41 @@ apply plugin: 'java' -test { - testLogging { - events "passed", "skipped", "failed" +sourceSets { + test { + java { + srcDirs = ['src/test/java'] + } + resources { + srcDirs = ['src/test/resources'] + } } } jar.baseName = 'leekscript' libsDirName = '..' +jar { + baseName = 'leekscript' + manifest { + attributes "Main-Class": "leekscript.TopLevel" + } + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } +} + repositories { - jcenter() + mavenCentral() } dependencies { - compile 'org.slf4j:slf4j-api:1.7.21' - testCompile 'junit:junit:4.12' - compile group: 'com.alibaba', name: 'fastjson', version: '1.1.25' + testImplementation 'junit:junit:4.12' + implementation group: 'com.alibaba', name: 'fastjson', version: '1.1.25' +} + +task tests(type: Exec) { + group = "Execution" + description = "Run the main class with ExecTask" + commandLine "java", "-classpath", sourceSets.test.runtimeClasspath.getAsPath(), "test.TestMain" } +test.dependsOn(tests) diff --git a/src/main/java/leekscript/AILog.java b/src/main/java/leekscript/AILog.java index 1cbce144..972e0899 100644 --- a/src/main/java/leekscript/AILog.java +++ b/src/main/java/leekscript/AILog.java @@ -9,26 +9,10 @@ public class AILog { public final static int STANDARD = 1; public final static int WARNING = 2; public final static int ERROR = 3; - public final static int MARK = 4; - public final static int PAUSE = 5; public final static int SSTANDARD = 6; public final static int SWARNING = 7; public final static int SERROR = 8; - - // Clés - public static final String DEPRECATED_FUNCTION = "deprecated_function"; - public static final String UNKNOWN_FUNCTION = "unknown_function"; - public static final String DIVISION_BY_ZERO = "division_by_zero"; - public static final String CAN_NOT_EXECUTE_VALUE = "can_not_execute_value"; - public static final String CAN_NOT_EXECUTE_WITH_ARGUMENTS = "can_not_execute_with_arguments"; - public static final String CAN_NOT_COMPILE_AI = "can_not_compile_ai"; - public static final String AI_DISABLED = "ai_disabled"; - public static final String AI_INTERRUPTED = "ai_interrupted"; - public static final String AI_TIMEOUT = "ai_timeout"; - public static final String CODE_TOO_LARGE = "code_too_large"; - public static final String CODE_TOO_LARGE_FUNCTION = "code_too_large_function"; - public static final String NUMBER_OF_OPERATIONS = "number_of_operations"; - + public interface Stream { public void write(JSONArray a); } @@ -46,8 +30,8 @@ public void write(JSONArray a) { }; } - public void addSystemLog(int type, String trace, String key, String[] parameters) { - + public void addSystemLog(int type, String trace, int key, String[] parameters) { + int paramSize = 0; if (parameters != null) { for (String p : parameters) { @@ -55,7 +39,7 @@ public void addSystemLog(int type, String trace, String key, String[] parameters } } - if (!addSize(20 + trace.length() + key.length() + paramSize)) { + if (!addSize(20 + trace.length() + paramSize)) { return; } @@ -66,12 +50,12 @@ public void addSystemLog(int type, String trace, String key, String[] parameters obj.add(key); if (parameters != null) obj.add(parameters); - + stream.write(obj); } public void addLog(int type, String message) { - + message = message.replaceAll("\t", " "); addLog(type, message, 0); } diff --git a/src/main/java/leekscript/LSException.java b/src/main/java/leekscript/LSException.java index 228cb0e2..8462bf8c 100644 --- a/src/main/java/leekscript/LSException.java +++ b/src/main/java/leekscript/LSException.java @@ -1,16 +1,14 @@ package leekscript; -import leekscript.runner.values.AbstractLeekValue; - public class LSException extends Exception { private static final long serialVersionUID = -8672880192298794957L; private final int mIndex; - private final AbstractLeekValue mRun; - private final AbstractLeekValue mThe; + private final Object mRun; + private final Object mThe; - public LSException(int i, AbstractLeekValue run, AbstractLeekValue the) { + public LSException(int i, Object run, Object the) { mIndex = i; mRun = run; mThe = the; @@ -20,11 +18,11 @@ public int getIndex() { return mIndex; } - public AbstractLeekValue getRun() { + public Object getRun() { return mRun; } - public AbstractLeekValue getThe() { + public Object getThe() { return mThe; } diff --git a/src/main/java/leekscript/TopLevel.java b/src/main/java/leekscript/TopLevel.java index d498536a..c7fd4e83 100644 --- a/src/main/java/leekscript/TopLevel.java +++ b/src/main/java/leekscript/TopLevel.java @@ -1,46 +1,72 @@ package leekscript; import java.util.Scanner; +import java.io.File; import leekscript.compiler.LeekScript; import leekscript.runner.AI; -import leekscript.runner.values.AbstractLeekValue; public class TopLevel { public static void main(String[] args) { if (args.length < 1) { Scanner input = new Scanner(System.in); - System.out.print(">>> "); - - String code; - while ((code = input.nextLine()) != null) { - execute(code); - System.out.print(">>> "); + System.out.print(">>> "); + String code; + while ((code = input.nextLine()) != null) { + executeSnippet(code); + System.out.print(">>> "); } - input.close(); + input.close(); } else { - String code = args[0]; - execute(code); + + File file = new File(args[0]); + if (file.exists()) { + executeFile(file); + } else { + executeSnippet(args[0]); + } } } - - private static void execute(String code) { + + private static void executeSnippet(String code) { try { long ct = System.currentTimeMillis(); - AI ai = LeekScript.compileSnippet(code, "AI", "leekscript.jar"); + AI ai = LeekScript.compileSnippet(code, "AI"); long compileTime = System.currentTimeMillis() - ct; - + long et = System.currentTimeMillis(); - AbstractLeekValue v = ai.runIA(); + ai.staticInit(); + var v = ai.runIA(); long executionTime = System.currentTimeMillis() - et; - - String result = v.getString(ai); - long ops = ai.getOperations(); - + + var result = ai.string(v); + long ops = ai.operations(); + System.out.println(result); System.out.println("(" + ops + " ops, " + compileTime + "ms + " + executionTime + "ms)"); - + + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static void executeFile(File file) { + try { + long ct = System.currentTimeMillis(); + AI ai = LeekScript.compileFile(file.getPath(), "AI", false); + long compileTime = System.currentTimeMillis() - ct; + + long et = System.currentTimeMillis(); + var v = ai.runIA(); + long executionTime = System.currentTimeMillis() - et; + + var result = ai.string(v); + long ops = ai.operations(); + + System.out.println(result); + System.out.println("(" + ops + " ops, " + compileTime + "ms + " + executionTime + "ms)"); + } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/leekscript/common/AccessLevel.java b/src/main/java/leekscript/common/AccessLevel.java new file mode 100644 index 00000000..51b38022 --- /dev/null +++ b/src/main/java/leekscript/common/AccessLevel.java @@ -0,0 +1,14 @@ +package leekscript.common; + +public enum AccessLevel { + PUBLIC, PROTECTED, PRIVATE; + + public static AccessLevel fromString(String word) { + switch (word) { + case "public": return AccessLevel.PUBLIC; + case "protected": return AccessLevel.PROTECTED; + case "private": return AccessLevel.PRIVATE; + } + return AccessLevel.PUBLIC; + } +} \ No newline at end of file diff --git a/src/main/java/leekscript/common/Error.java b/src/main/java/leekscript/common/Error.java new file mode 100644 index 00000000..2caa3e66 --- /dev/null +++ b/src/main/java/leekscript/common/Error.java @@ -0,0 +1,97 @@ +package leekscript.common; + +public enum Error { + NONE, + FUNCTION_NAME_UNAVAILABLE, // 1 + PARAMETER_NAME_UNAVAILABLE, // 2 + OPENING_PARENTHESIS_EXPECTED, // 3 + OPENING_CURLY_BRACKET_EXPECTED, // 4 + PARAMETER_NAME_EXPECTED, // 5 + FUNCTION_NAME_EXPECTED, // 6 + PARENTHESIS_EXPECTED_AFTER_PARAMETERS, // 7 + OPEN_BLOC_REMAINING, // 8 + NO_BLOC_TO_CLOSE, // 9 + END_OF_SCRIPT_UNEXPECTED, // 10 + END_OF_INSTRUCTION_EXPECTED, // 11 + BREAK_OUT_OF_LOOP, // 12 + CONTINUE_OUT_OF_LOOP, // 13 + INCLUDE_ONLY_IN_MAIN_BLOCK, // 14 + AI_NAME_EXPECTED, // 15 + AI_NOT_EXISTING, // 16 + CLOSING_PARENTHESIS_EXPECTED, // 17 + CLOSING_SQUARE_BRACKET_EXPECTED, // 18 + FUNCTION_ONLY_IN_MAIN_BLOCK, // 19 + VARIABLE_NAME_EXPECTED, // 20 + VARIABLE_NAME_UNAVAILABLE, // 21 + VARIABLE_NOT_EXISTS, // 22 + KEYWORD_UNEXPECTED, // 23 + KEYWORD_IN_EXPECTED, // 24 + WHILE_EXPECTED_AFTER_DO, // 25 + NO_IF_BLOCK, // 26 + GLOBAL_ONLY_IN_MAIN_BLOCK, // 27 + VAR_NAME_EXPECTED_AFTER_GLOBAL, // 28 + VAR_NAME_EXPECTED, // 29 + SIMPLE_ARRAY, // 30 + ASSOCIATIVE_ARRAY, // 31 + PARENTHESIS_EXPECTED_AFTER_FUNCTION, // 32 + UNKNOWN_VARIABLE_OR_FUNCTION, // 33 + OPERATOR_UNEXPECTED, // 34 + VALUE_EXPECTED, // 35 + CANT_ADD_INSTRUCTION_AFTER_BREAK, // 36 + UNCOMPLETE_EXPRESSION, // 37 + CANT_ASSIGN_VALUE, // 38 + FUNCTION_NOT_EXISTS, // 39 + INVALID_PARAMETER_COUNT, // 40 + INVALID_CHAR, // 41 + INVALID_NUMBER, // 42 + CONSTRUCTOR_ALREADY_EXISTS, // 43 + END_OF_CLASS_EXPECTED, // 44 + FIELD_ALREADY_EXISTS, // 45 + NO_SUCH_CLASS, // 46 + THIS_NOT_ALLOWED_HERE, // 47 + KEYWORD_MUST_BE_IN_CLASS, // 48 + SUPER_NOT_AVAILABLE_PARENT, // 49 + CLASS_MEMBER_DOES_NOT_EXIST, // 50 + CLASS_STATIC_MEMBER_DOES_NOT_EXIST, // 51 + EXTENDS_LOOP, // 52 + REFERENCE_DEPRECATED, // 53 + DUPLICATED_METHOD, // 54 + DEPRECATED_FUNCTION, // 55 + UNKNOWN_FUNCTION, // 56 + DIVISION_BY_ZERO, // 57 + CAN_NOT_EXECUTE_VALUE, // 58 + CAN_NOT_EXECUTE_WITH_ARGUMENTS, // 59 + NO_AI_EQUIPPED, // 60 + INVALID_AI, // 61 + COMPILE_JAVA, // 62 + AI_DISABLED, // 63 + AI_INTERRUPTED, // 64 + AI_TIMEOUT, // 65 + CODE_TOO_LARGE, // 66 + CODE_TOO_LARGE_FUNCTION, // 67 + INTERNAL_ERROR, // 68 + UNKNOWN_METHOD, // 69 + UNKNOWN_STATIC_METHOD, // 70 + STRING_METHOD_MUST_RETURN_STRING, // 71 + UNKNOWN_FIELD, // 72 + UNKNOWN_CONSTRUCTOR, // 73 + INSTANCEOF_MUST_BE_CLASS, // 74 + NOT_ITERABLE, // 75 + STACKOVERFLOW, // 76 + INVALID_OPERATOR, // 77 + PRIVATE_FIELD, // 78 + PROTECTED_FIELD, // 79 + PRIVATE_STATIC_FIELD, // 80 + PROTECTED_STATIC_FIELD, // 81 + PRIVATE_METHOD, // 82 + PROTECTED_METHOD, // 83 + PRIVATE_CONSTRUCTOR, // 84 + PROTECTED_CONSTRUCTOR, // 85 + PRIVATE_STATIC_METHOD, // 86 + PROTECTED_STATIC_METHOD, // 87 + CANNOT_LOAD_AI, // 88 + TRANSPILE_TO_JAVA, // 89 + CANNOT_WRITE_AI, // 90 + RESERVED_FIELD, // 91 + VALUE_IS_NOT_AN_ARRAY, // 92 +} \ No newline at end of file diff --git a/src/main/java/leekscript/common/Type.java b/src/main/java/leekscript/common/Type.java new file mode 100644 index 00000000..70a3127a --- /dev/null +++ b/src/main/java/leekscript/common/Type.java @@ -0,0 +1,44 @@ +package leekscript.common; + +public class Type { + + public static Type ANY = new Type("any", '?'); + public static Type NULL = new Type("null", 'u'); + public static Type BOOL = new Type("bool", 'b'); + public static Type NUMBER = new Type("number", 'n'); + public static Type INT = new Type("int", 'i'); + public static Type REAL = new Type("real", 'r'); + public static Type STRING = new Type("string", 's'); + public static Type ARRAY = new Type("array", 'a'); + public static Type OBJECT = new Type("object", 'o'); + public static Type FUNCTION = new Type("function", 'f'); + + public String name; + public char signature; + + public Type(String name, char signature) { + this.name = name; + this.signature = signature; + } + + public boolean accepts(Type type) { + if (type == this) return true; + if (this == ANY) return true; + if (this == NUMBER) { + if (type == BOOL || type == INT || type == REAL) return true; + } + if (this == REAL) { + if (type == INT) return true; + } + if (this == BOOL) return true; + return false; + } + + public boolean isNumber() { + return this == NUMBER || this == INT || this == REAL; + } + + public char getSignature() { + return signature; + } +} diff --git a/src/main/java/leekscript/compiler/AICode.java b/src/main/java/leekscript/compiler/AICode.java new file mode 100644 index 00000000..78f1e148 --- /dev/null +++ b/src/main/java/leekscript/compiler/AICode.java @@ -0,0 +1,19 @@ +package leekscript.compiler; + +public class AICode { + String javaCode; + String linesFile; + + public AICode(String javaCode, String linesFile) { + this.javaCode = javaCode; + this.linesFile = linesFile; + } + + public String getJavaCode() { + return javaCode; + } + + public String getLines() { + return linesFile; + } +} \ No newline at end of file diff --git a/src/main/java/leekscript/compiler/AIFile.java b/src/main/java/leekscript/compiler/AIFile.java index 3d51b043..a14f94d4 100644 --- a/src/main/java/leekscript/compiler/AIFile.java +++ b/src/main/java/leekscript/compiler/AIFile.java @@ -1,18 +1,35 @@ package leekscript.compiler; +import com.alibaba.fastjson.JSONObject; + import leekscript.compiler.resolver.ResolverContext; public class AIFile { - + private String path; private String code; private C context; - private String javaClassName; - - public AIFile(String path, String code, C context) { + private int id; + private long timestamp; + private int version; + + public AIFile(String path, String code, long timestamp, int version, C context) { + this(path, code, timestamp, version, context, (context + "/" + path).hashCode() & 0xfffffff); + } + + public AIFile(String path, String code, long timestamp, int version, C context, int id) { this.path = path; this.code = code; this.context = context; + this.timestamp = timestamp; + this.version = version; + this.id = id; + } + public int getId() { + return id; + } + public void setId(int id) { + this.id = id; } public String getCode() { return code; @@ -26,16 +43,31 @@ public C getContext() { public void setContext(C context) { this.context = context; } - public String getJavaClassName() { - return javaClassName; - } - public void setJavaClassName(String javaClassName) { - this.javaClassName = javaClassName; - } public String getPath() { return path; } public void setPath(String path) { this.path = path; } + public long getTimestamp() { + return this.timestamp; + } + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + public int getVersion() { + return this.version; + } + public void setVersion(int version) { + this.version = version; + } + + public String toJson() { + JSONObject json = new JSONObject(); + json.put("path", path); + json.put("timestamp", timestamp); + json.put("version", version); + context.toJson(json); + return json.toJSONString(); + } } diff --git a/src/main/java/leekscript/compiler/AnalyzeError.java b/src/main/java/leekscript/compiler/AnalyzeError.java new file mode 100644 index 00000000..16a413c9 --- /dev/null +++ b/src/main/java/leekscript/compiler/AnalyzeError.java @@ -0,0 +1,65 @@ +package leekscript.compiler; + +import com.alibaba.fastjson.JSONArray; +import leekscript.common.Error; + +public class AnalyzeError implements Comparable { + + public static enum AnalyzeErrorLevel { + ERROR, // 0 + WARNING, // 1 + } + + // public String file; + // public int startLine; + // public int startCharacter; + // public int endLine; + // public int endCharacter; + public IAWord token; + public Error error; + public AnalyzeErrorLevel level; + public String[] parameters; + + public AnalyzeError(IAWord token, AnalyzeErrorLevel level, Error error) { + this(token, level, error, null); + } + public AnalyzeError(IAWord token, AnalyzeErrorLevel level, Error error, String[] parameters) { + this.token = token; + this.error = error; + this.level = level; + this.parameters = parameters; + } + + public JSONArray toJSON() { + JSONArray array = new JSONArray(); + array.add(level.ordinal()); + array.add(token.getAI().getId()); + array.add(token.getLine()); + array.add(token.getCharacter()); + array.add(token.getWord()); + array.add(this.error.ordinal()); + if (parameters != null) { + array.add(parameters); + } + return array; + } + + @Override + public int compareTo(AnalyzeError o) { + if (token.getLine() != o.token.getLine()) { + return token.getLine() - o.token.getLine(); + } + if (token.getCharacter() != o.token.getCharacter()) { + return token.getCharacter() - o.token.getCharacter(); + } + return 0; + } + + // public AnalyzeError(String file, int startLine, int startCharacter, int endLine, int endCharacter) { + // this.file = file; + // this.startLine = startLine; + // this.startCharacter = startCharacter; + // this.endLine = endLine; + // this.endCharacter = endCharacter; + // } +} diff --git a/src/main/java/leekscript/compiler/IACompiler.java b/src/main/java/leekscript/compiler/IACompiler.java index 993795d9..716a0d2f 100644 --- a/src/main/java/leekscript/compiler/IACompiler.java +++ b/src/main/java/leekscript/compiler/IACompiler.java @@ -1,8 +1,12 @@ package leekscript.compiler; -import leekscript.ErrorManager; +import leekscript.compiler.AnalyzeError.AnalyzeErrorLevel; import leekscript.compiler.bloc.MainLeekBlock; import leekscript.compiler.exceptions.LeekCompilerException; +import leekscript.common.Error; + +import java.util.ArrayList; +import java.util.List; import com.alibaba.fastjson.JSONArray; @@ -11,76 +15,105 @@ */ public class IACompiler { + public static class AnalyzeResult { + public JSONArray informations; + public List includedAIs = new ArrayList<>(); + public boolean success; + } + private final JSONArray mInformations = new JSONArray(); - private boolean mErrors = false; private AIFile mCurrentAI; public IACompiler() {} - public void addError(AIFile ia_context, int line, int pos, String word, String informations, String[] parameters) { - mErrors = true; + public void addError(AIFile ia_context, int line, int pos, String word, Error errorType, String[] parameters) { JSONArray error = new JSONArray(); - error.add(0); - error.add(ia_context); + error.add(0); // level + error.add(ia_context.getId()); error.add(line); error.add(pos); error.add(word); - error.add(informations); + error.add(errorType.ordinal()); if (parameters != null) error.add(parameters); mInformations.add(error); } - public void addError(AIFile ia_context, String informations) { - mErrors = true; - JSONArray error = new JSONArray(); - error.add(1); - error.add(ia_context); - error.add(informations); - mInformations.add(error); - } + public AnalyzeResult analyze(AIFile ai) throws LeekCompilerException { + AnalyzeResult result = new AnalyzeResult(); + try { + // On lance la compilation du code de l'IA + WordParser parser = new WordParser(ai, ai.getVersion()); + // Si on est là c'est qu'on a une liste de words correcte, on peut commencer à lire + MainLeekBlock main = new MainLeekBlock(this, ai); + WordCompiler compiler = new WordCompiler(parser, main, ai, ai.getVersion()); + main.setWordCompiler(compiler); + compiler.readCode(); + compiler.analyze(); - public void addInformations(AIFile ia_context, int level) { - JSONArray error = new JSONArray(); - error.add(2); - error.add(ia_context); - error.add(level); - mInformations.add(error); + // System.out.println("errors " + compiler.getErrors().size()); + if (compiler.getErrors().size() > 0) { + for (var error : compiler.getErrors()) { + mInformations.add(error.toJSON()); + } + result.success = false; + } else { + result.includedAIs = main.getIncludedAIs(); + result.success = true; + } + } catch (LeekCompilerException e) { + addError(e.getIA(), e.getLine(), e.getChar(), e.getWord(), e.getError(), e.getParameters()); + result.success = false; + } + result.informations = mInformations; + return result; } - public String compile(AIFile ai, String AIClass) throws LeekCompilerException { - JavaWriter writer = new JavaWriter(true); + public AICode compile(AIFile ai, String javaClassName, String AIClass) throws LeekCompilerException { + JavaWriter writer = new JavaWriter(true, javaClassName); try { // On lance la compilation du code de l'IA - WordParser parser = new WordParser(ai); + WordParser parser = new WordParser(ai, ai.getVersion()); // Si on est là c'est qu'on a une liste de words correcte, on peut commencer à lire MainLeekBlock main = new MainLeekBlock(this, ai); - WordCompiler compiler = new WordCompiler(parser, main, ai); + WordCompiler compiler = new WordCompiler(parser, main, ai, ai.getVersion()); + main.setWordCompiler(compiler); compiler.readCode(); + compiler.analyze(); + // System.out.println("errors " + compiler.getErrors().size()); - compiler.writeJava(ai.getJavaClassName(), writer, AIClass); - - // On sauvegarde les dépendances - addInformations(ai, main.getMinLevel()); + if (compiler.getErrors().size() > 0) { + for (var error : compiler.getErrors()) { + if (error.level == AnalyzeErrorLevel.ERROR) { + throw new LeekCompilerException(error.token, error.error, error.parameters); + } + } + } + compiler.writeJava(javaClassName, writer, AIClass); } catch (LeekCompilerException e) { addError(e.getIA(), e.getLine(), e.getChar(), e.getWord(), e.getError(), e.getParameters()); throw e; - } catch (Exception e) { - ErrorManager.exception(e); - addError(ai, e.getMessage()); } - return writer.getJavaCode(); + return writer.getCode(); + } + + public String merge(AIFile ai) throws LeekCompilerException { + // System.out.println("Merge ai " + ai); + WordParser parser = new WordParser(ai, ai.getVersion()); + MainLeekBlock main = new MainLeekBlock(this, ai); + WordCompiler compiler = new WordCompiler(parser, main, ai, ai.getVersion()); + main.setWordCompiler(compiler); + compiler.readCode(); + String code = main.getCode(); + // System.out.println("Code = " + code); + return code; } public String getInformations() { return mInformations.toJSONString(); } - public boolean hasError() { - return mErrors; - } - public AIFile getCurrentAI() { return mCurrentAI; } diff --git a/src/main/java/leekscript/compiler/IALoader.java b/src/main/java/leekscript/compiler/IALoader.java deleted file mode 100644 index 3f51cd59..00000000 --- a/src/main/java/leekscript/compiler/IALoader.java +++ /dev/null @@ -1,33 +0,0 @@ -package leekscript.compiler; - -import java.io.File; -import java.net.URL; -import java.net.URLClassLoader; - -import leekscript.ErrorManager; -import leekscript.runner.AI; - -public class IALoader { - - @SuppressWarnings("resource") - public static AI loadAI(String file, String classname) { - - File f = new File(file); - if (!f.exists()) - return null; - - URLClassLoader loader = null; - try { - // Not closed because we need to load internal classes, like Class$1.class etc. - loader = new URLClassLoader(new URL[] { f.toURI().toURL() }, new ClassLoader() {}); - - if (loader != null) { - Class c = loader.loadClass(classname); - return (AI) c.newInstance(); - } - } catch (Exception e) { - ErrorManager.exception(e); - } - return null; - } -} diff --git a/src/main/java/leekscript/compiler/IAWord.java b/src/main/java/leekscript/compiler/IAWord.java index 5dd65ba8..4deb8206 100644 --- a/src/main/java/leekscript/compiler/IAWord.java +++ b/src/main/java/leekscript/compiler/IAWord.java @@ -1,12 +1,21 @@ package leekscript.compiler; public class IAWord { + private final int type; private final String word; private final int line; private final int character; private final AIFile ai; + public IAWord(String word) { + this.ai = null; + this.type = 0; + this.word = word; + this.line = 0; + this.character = 0; + } + public IAWord(AIFile ai, int type, String word, int line, int character) { this.ai = ai; this.type = type; @@ -34,4 +43,9 @@ public int getLine() { public int getCharacter() { return character; } + + @Override + public String toString() { + return word; + } } diff --git a/src/main/java/leekscript/compiler/JavaCompiler.java b/src/main/java/leekscript/compiler/JavaCompiler.java deleted file mode 100644 index 5b226da5..00000000 --- a/src/main/java/leekscript/compiler/JavaCompiler.java +++ /dev/null @@ -1,97 +0,0 @@ -package leekscript.compiler; - -import java.io.File; -import java.io.InputStream; -import java.util.Arrays; - -public class JavaCompiler { - - public final static int INIT = 0; - public final static int RUNNING = 1; - public final static int END = 2; - public final static int ERROR = 3; - private final File mInput; - private int mStatus = 0; - - private static class Worker extends Thread { - private final Process process; - private Integer exit; - - private Worker(Process process) { - this.process = process; - } - - public void run() { - try { - exit = process.waitFor(); - } catch (InterruptedException ignore) { - return; - } - } - } - - public JavaCompiler(File input) { - mInput = input; - } - - public void compile(String jar) throws Exception { - if (mStatus != INIT) - return; - mStatus = RUNNING; - if (!mInput.exists()) { - mStatus = ERROR; - return; - } - - Process process = Runtime.getRuntime().exec(new String[] { "javac", "-encoding", "utf8", "-nowarn", "-classpath", jar, mInput.getAbsolutePath() }); - - Worker worker = new Worker(process); - worker.start(); - try { - - worker.join(10000); - if (worker.exit == null) { - throw new CompilationException("too_long_java"); - } - - // new - // File(System.getProperty("user.dir"))); - boolean error = false; - InputStream iin = process.getErrorStream(); - if (iin != null) { - byte[] e = new byte[128]; - int nb; - StringBuilder sb = new StringBuilder(); - while ((nb = iin.read(e)) > 0) { - sb.append(new String(Arrays.copyOf(e, nb))); - } - if (sb.length() > 0) - throw new CompilationException(sb.toString()); - } - iin = process.getInputStream(); - if (iin != null) { - byte[] e = new byte[128]; - int nb; - while ((nb = iin.read(e)) > 0) { - System.out.println(new String(Arrays.copyOf(e, nb))); - } - } - process.waitFor(); - if (process.exitValue() != 0) - error = true; - if (!error) - mStatus = END; - - } catch (InterruptedException ex) { - worker.interrupt(); - Thread.currentThread().interrupt(); - throw ex; - } finally { - process.destroy(); - } - } - - public static int getStatus() { - return 0; - } -} diff --git a/src/main/java/leekscript/compiler/JavaWriter.java b/src/main/java/leekscript/compiler/JavaWriter.java index d53cab26..4b09ff36 100644 --- a/src/main/java/leekscript/compiler/JavaWriter.java +++ b/src/main/java/leekscript/compiler/JavaWriter.java @@ -1,30 +1,33 @@ package leekscript.compiler; import java.util.ArrayList; +import java.util.HashMap; +import java.util.TreeMap; + +import com.alibaba.fastjson.JSON; + +import leekscript.compiler.bloc.AbstractLeekBlock; +import leekscript.common.Type; +import leekscript.compiler.bloc.MainLeekBlock; +import leekscript.compiler.expression.AbstractExpression; public class JavaWriter { private final StringBuilder mCode; + private final StringBuilder mLinesFile; private int mLine; - private final ArrayList mLines; + private final TreeMap mLines = new TreeMap<>(); + private final HashMap, Integer> mFiles = new HashMap<>(); + private final ArrayList> mFilesList = new ArrayList<>(); private final boolean mWithDebug; + private final String className; + public AbstractLeekBlock currentBlock = null; - private class Line { - private final int mJavaLine; - private final int mCodeLine; - private final AIFile mAI; - - public Line(int java_line, int code_line, AIFile ai) { - mJavaLine = java_line; - mCodeLine = code_line; - mAI = ai; - } - } - - public JavaWriter(boolean debug) { + public JavaWriter(boolean debug, String className) { mCode = new StringBuilder(); - mLines = new ArrayList(); + mLinesFile = new StringBuilder(); mLine = 1; mWithDebug = debug; + this.className = className; } public boolean hasDebug() { @@ -33,60 +36,129 @@ public boolean hasDebug() { public void addLine(String datas, int line, AIFile ai) { mCode.append(datas).append("\n"); - mLines.add(new Line(mLine, line, ai)); + int fileIndex = getFileIndex(ai); + mLines.put(mLine, new LineMapping(line, fileIndex)); mLine++; } + private int getFileIndex(AIFile ai) { + var index = mFiles.get(ai); + if (index != null) return index; + var new_index = mFiles.size(); + mFiles.put(ai, new_index); + mFilesList.add(ai); + return new_index; + } + public void addLine(String datas) { mCode.append(datas).append("\n"); mLine++; } + public void addLine() { + mCode.append("\n"); + mLine++; + } + public void addCode(String datas) { mCode.append(datas); } - public String getJavaCode() { - return mCode.toString(); - } - - public String escape(String string) { - String str = ""; - for (int i = 0; i < string.length(); i++) { - if (string.charAt(i) == '\n') - str += "\\n"; - else if (string.charAt(i) == '"') - str += "\\\""; - else if (string.charAt(i) == '\\') { - if (string.charAt(i + 1) == 'n') - str += "\\"; - else if (string.charAt(i + 1) == 't') - str += "\\"; - else - str += "\\\\"; - } - else - str += string.charAt(i); - } - return str; + public AICode getCode() { + return new AICode(mCode.toString(), mLinesFile.toString()); } public void writeErrorFunction(IACompiler comp, String ai) { - mCode.append("protected String getErrorString(){ return \"["); - boolean first = true; - for (Line l : mLines) { - if (!first) - mCode.append(","); - else - first = false; - mCode.append("[").append(l.mJavaLine).append(",\\\"").append(escape(l.mAI.getPath())).append("\\\",").append(l.mCodeLine).append("]"); + String aiJson = JSON.toJSONString(ai); + for (var e : mLines.entrySet()) { + var line = e.getValue(); + mLinesFile.append(e.getKey() + " " + line.getAI() + " " + line.getLeekScriptLine() + "\n"); + // System.out.println(l.mAI.getPath() + ":" + l.mCodeLine + " -> " + l.mJavaLine); } - mCode.append("]\";}\n protected String getAItring(){ return \""); - mCode.append(escape(ai)); - mCode.append("\";}"); + mCode.append("protected String getAIString() { return "); + mCode.append(aiJson); + mCode.append(";}\n"); + + mCode.append("protected String[] getErrorFiles() { return new String[] {"); + for (var f : mFilesList) { + mCode.append("\"" + f.getPath().replaceAll("\\\\/", "/").replaceAll("\\\\", "\\\\\\\\").replaceAll("\"", "\\\\\"") + "\""); + mCode.append(", "); + } + mCode.append("};}\n"); } - public void addCounter(int id) { - addCode("mUAI.addOperations(1);"); + public void addCounter(int count) { + addCode("ops(" + count + ");"); + } + + public int getCurrentLine() { + return mLine; + } + + public void addPosition(IAWord token) { + var index = getFileIndex(token.getAI()); + mLines.put(mLine, new LineMapping(token.getLine(), index)); + } + + public String getAIThis() { + return className + ".this"; + } + + public String getClassName() { + return className; + } + + public void getBoolean(MainLeekBlock mainblock, AbstractExpression expression) { + if (expression.getType() == Type.BOOL) { + expression.writeJavaCode(mainblock, this); + } else if (expression.getType() == Type.INT) { + addCode("(("); + expression.writeJavaCode(mainblock, this); + addCode(") != 0)"); + } else { + addCode("bool("); + expression.writeJavaCode(mainblock, this); + addCode(")"); + } + } + + public void getString(MainLeekBlock mainblock, AbstractExpression expression) { + if (expression.getType() == Type.STRING) { + expression.writeJavaCode(mainblock, this); + } else { + addCode("string("); + expression.writeJavaCode(mainblock, this); + addCode(")"); + } + } + + public void getInt(MainLeekBlock mainblock, AbstractExpression expression) { + if (expression.getType() == Type.INT) { + expression.writeJavaCode(mainblock, this); + } else { + addCode("integer("); + expression.writeJavaCode(mainblock, this); + addCode(")"); + } + } + + public void compileLoad(MainLeekBlock mainblock, AbstractExpression expr) { + if (expr.getType() == Type.NULL || expr.getType() == Type.BOOL || expr.getType().isNumber() || expr.getType() == Type.STRING || expr.getType() == Type.ARRAY) { + expr.writeJavaCode(mainblock, this); + } else { + addCode("load("); + expr.writeJavaCode(mainblock, this); + addCode(")"); + } + } + + public void compileClone(MainLeekBlock mainblock, AbstractExpression expr) { + if (expr.getType() == Type.NULL || expr.getType() == Type.BOOL || expr.getType().isNumber() || expr.getType() == Type.STRING) { + expr.writeJavaCode(mainblock, this); + } else { + addCode("copy("); + expr.writeJavaCode(mainblock, this); + addCode(")"); + } } } diff --git a/src/main/java/leekscript/compiler/LeekScript.java b/src/main/java/leekscript/compiler/LeekScript.java index 10ce5b69..ec63ef24 100644 --- a/src/main/java/leekscript/compiler/LeekScript.java +++ b/src/main/java/leekscript/compiler/LeekScript.java @@ -2,172 +2,284 @@ import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; +import java.io.StringWriter; +import java.nio.file.Paths; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; import java.util.Random; -import leekscript.ErrorManager; -import leekscript.LSException; +import javax.tools.JavaCompiler; +import javax.tools.ToolProvider; + import leekscript.compiler.exceptions.LeekCompilerException; import leekscript.compiler.resolver.FileSystemContext; import leekscript.compiler.resolver.FileSystemResolver; import leekscript.compiler.resolver.Resolver; +import leekscript.compiler.resolver.ResolverContext; +import leekscript.compiler.resolver.ResourceContext; +import leekscript.compiler.resolver.ResourceResolver; import leekscript.runner.AI; -import leekscript.runner.values.AbstractLeekValue; -import leekscript.runner.values.ArrayLeekValue; +import leekscript.common.Error; public class LeekScript { - - private final static String IA_PATH = "ai/"; + + private final static String IA_PATH = "ai"; private static long id = 1; - - private static Resolver defaultResolver = new FileSystemResolver(); + + private static class AIClassEntry { + Class clazz; + long timestamp; + public AIClassEntry(Class clazz, long timestamp) { + this.clazz = clazz; + this.timestamp = timestamp; + } + } + + private static Resolver defaultResolver = new ResourceResolver(); + private static Resolver fileSystemResolver = new FileSystemResolver(); private static Resolver customResolver = null; - + private static String classpath; + private static List arguments = new ArrayList<>(); + private static JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + private static URLClassLoader urlLoader; + private static HashMap aiCache = new HashMap<>(); + static { + classpath = new File(LeekScript.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getPath(); + arguments.addAll(Arrays.asList("-classpath", classpath, "-nowarn")); + try { + urlLoader = new URLClassLoader(new URL[] { new File(IA_PATH).toURI().toURL() }, new ClassLoader() {}); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + } + private static RandomGenerator defaultRandomGenerator = new RandomGenerator() { private Random random = new Random(); + @Override public void seed(long seed) { random.setSeed(seed); } + @Override public int getInt(int min, int max) { if (max - min + 1 <= 0) return 0; return min + random.nextInt(max - min + 1); } + @Override public double getDouble() { return random.nextDouble(); } }; - private static RandomGenerator customRandomGenerator = null; - - public static AI compileFile(String filepath, String AIClass, String jar, boolean nocache) throws LeekScriptException, LeekCompilerException { + + public static AI compileFile(String filepath, String AIClass, boolean useClassCache) throws LeekScriptException, LeekCompilerException, IOException { AIFile ai = getResolver().resolve(filepath, null); - if (ai != null) { - int id = (filepath + "_" + ai.getCode()).hashCode() & 0xfffffff; - ai.setJavaClassName("IA_" + id); - return compile(ai, AIClass, jar, nocache); - } - return null; - } - - public static AI compileSnippet(String snippet, String AIClass, String jar) throws LeekScriptException, LeekCompilerException { - AIFile ai = new AIFile("", snippet, null); - ai.setJavaClassName("IA_" + id++); - return compile(ai, AIClass, jar, false); - } - - public static boolean testScript(String leek, String script, AbstractLeekValue s, String AIClass, String jar, boolean nocache) throws Exception { - AI ai = LeekScript.compileSnippet(script, AIClass, jar); - AbstractLeekValue v = ai.runIA(); - if (v.equals(ai, s)) - return true; - ArrayLeekValue tab1 = v.getArray(); - ArrayLeekValue tab2 = s.getArray(); - if (tab1 != null && tab2 != null && tab1.size() == tab2.size()) { - int i = 0; - for (i = 0; i < tab1.size(); i++) { - if (!tab1.get(ai, i).equals(ai, tab2.get(ai, i))) { - throw new LSException(i, tab1.get(ai, i), tab2.get(ai, i)); - } - } - } else - System.out.println(v.getString(ai) + " -- " + s.getString(ai)); - return false; - } - - public static AbstractLeekValue runScript(String script, boolean nocache) throws Exception { - return LeekScript.compileSnippet(script, "AI", "leekscript.jar").runIA(); - } - - public static boolean testScript(String script, AbstractLeekValue s) throws Exception { - AI ai = LeekScript.compileSnippet(script, "AI", "leekscript.jar"); - AbstractLeekValue v = ai.runIA(); - System.out.println(v.getString(ai)); - return v.equals(ai, s); - } - + return compile(ai, AIClass, useClassCache); + } + + public static AI compileFile(String filepath, String AIClass, int version) throws LeekScriptException, LeekCompilerException, IOException { + AIFile ai = getResolver().resolve(filepath, null); + ai.setVersion(version); + return compile(ai, AIClass, false); + } + + public static AI compileFileContext(String filepath, String AIClass, ResolverContext context, boolean useClassCache) throws LeekScriptException, LeekCompilerException, IOException { + AIFile ai = getResolver().resolve(filepath, context); + return compile(ai, AIClass, useClassCache); + } + + public static AI compileSnippet(String snippet, String AIClass) throws LeekScriptException, LeekCompilerException, IOException { + return compileSnippet(snippet, AIClass, 2); + } + + public static AI compileSnippet(String snippet, String AIClass, int version) throws LeekScriptException, LeekCompilerException, IOException { + long ai_id = id++; + AIFile ai = new AIFile("", snippet, System.currentTimeMillis(), version, null, (int) ai_id); + return compile(ai, AIClass, false); + } + + public static String mergeFile(String filepath, ResolverContext context) throws LeekScriptException, LeekCompilerException, IOException { + AIFile ai = getResolver().resolve(filepath, context); + + return new IACompiler().merge(ai); + } + + public static Object runScript(String script, boolean nocache) throws Exception { + return LeekScript.compileSnippet(script, "AI").runIA(); + } + public static String runFile(String filename) throws Exception { - AI ai = LeekScript.compileFile(filename, "AI", "leekscript.jar", true); - AbstractLeekValue v = ai.runIA(); - System.out.println(v.getString(ai)); - return v.getString(ai); + AI ai = LeekScript.compileFile(filename, "AI", true); + var v = ai.runIA(); + System.out.println(ai.string(v)); + return ai.string(v); } - + public static void setResolver(Resolver resolver) { customResolver = resolver; } + public static void resetResolver() { customResolver = null; } + public static Resolver getResolver() { return customResolver != null ? customResolver : defaultResolver; } - - public static void setRandomGenerator(RandomGenerator generator) { - customRandomGenerator = generator; - } + public static RandomGenerator getRandom() { - return customRandomGenerator != null ? customRandomGenerator : defaultRandomGenerator; - } - - private static AI compile(AIFile ai, String AIClass, String jar, boolean nocache) throws LeekScriptException, LeekCompilerException { - - new File(IA_PATH).mkdir(); - String javaClassName = ai.getJavaClassName(); - String error = ""; - File compiled = new File(IA_PATH + javaClassName + ".class"); - File java = new File(IA_PATH + javaClassName + ".java"); - - if (!compiled.exists() || nocache) { - // On commence par la conversion LS->Java - if (ai.getCode().isEmpty()) { // Pas de code du tout... - System.out.println("No code!"); - return null; - } - // On compile l'IA - String compiledJava = new IACompiler().compile(ai, AIClass); - - if (compiledJava.isEmpty()) { - System.out.println("No java generated!"); - return null; // Rien ne compile + return defaultRandomGenerator; + } + + public static AI compile(AIFile file, String AIClass, boolean useClassCache) throws LeekScriptException, LeekCompilerException { + + // System.out.println("LeekScript compile AI " + file.getPath() + " timestamp : " + file.getTimestamp()); + + var root = new File(IA_PATH); + if (!root.exists()) root.mkdir(); + String javaClassName = "AI_" + file.getId(); + String fileName = javaClassName + ".java"; + File compiled = Paths.get(IA_PATH, javaClassName + ".class").toFile(); + File java = Paths.get(IA_PATH, javaClassName + ".java").toFile(); + File lines = Paths.get(IA_PATH, javaClassName + ".lines").toFile(); + + // Cache des classes en RAM d'abord + var entry = aiCache.get(javaClassName); + if (entry != null && entry.timestamp >= file.getTimestamp()) { + // System.out.println("Load AI " + file.getPath() + " from RAM"); + try { + var ai = (AI) entry.clazz.getDeclaredConstructor().newInstance(); + ai.setId(file.getId()); + ai.setLinesFile(lines); + return ai; + } catch (Exception e) { + throw new LeekScriptException(Error.CANNOT_LOAD_AI, e.getMessage()); } - // Si on a maintenant du code java + } + + // Utilisation du cache de class dans le file system + if (useClassCache && compiled.exists() && compiled.length() != 0 && compiled.lastModified() >= file.getTimestamp()) { + // System.out.println("Load AI " + file.getPath() + " from disk"); try { - FileOutputStream output = new FileOutputStream(java); - output.write(compiledJava.getBytes()); - output.close(); + try { + urlLoader = new URLClassLoader(new URL[] { new File(IA_PATH).toURI().toURL() }, new ClassLoader() {}); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + var clazz = urlLoader.loadClass(javaClassName); + entry = new AIClassEntry(clazz, file.getTimestamp()); + aiCache.put(javaClassName, entry); + var ai = (AI) entry.clazz.getDeclaredConstructor().newInstance(); + ai.setId(file.getId()); + ai.setLinesFile(lines); + return ai; } catch (Exception e) { - ErrorManager.exception(e); - System.out.println("Failed to compiled AI: " + ai.getPath()); - return null; + throw new LeekScriptException(Error.CANNOT_LOAD_AI, e.getMessage()); + } + } + + // On commence par la conversion LS -> Java + // System.out.println("Re-compile AI " + file.getPath()); + long t = System.nanoTime(); + var compiledCode = new IACompiler().compile(file, javaClassName, AIClass); + long analyze_time = System.nanoTime() - t; + + if (compiledCode.getJavaCode().isEmpty()) { // Rien ne compile, pas normal + throw new LeekScriptException(Error.TRANSPILE_TO_JAVA, "No java generated!"); + } + + // System.out.println(compiledJava); + + // Sauvegarde du code java + try { + FileOutputStream javaOutput = new FileOutputStream(java); + javaOutput.write(compiledCode.getJavaCode().getBytes(StandardCharsets.UTF_8)); + javaOutput.close(); + } catch (IOException e) { + throw new LeekScriptException(Error.CANNOT_WRITE_AI, e.getMessage()); + } + + // Sauvegarde du fichier de lignes + try { + FileOutputStream javaOutput = new FileOutputStream(lines); + javaOutput.write(compiledCode.getLines().getBytes(StandardCharsets.UTF_8)); + javaOutput.close(); + } catch (IOException e) { + throw new LeekScriptException(Error.CANNOT_WRITE_AI, e.getMessage()); + } + + t = System.nanoTime(); + var fileManager = new SimpleFileManager(compiler.getStandardFileManager(null, null, null)); + var output = new StringWriter(); + var compilationUnits = Collections.singletonList(new SimpleSourceFile(fileName, compiledCode.getJavaCode())); + var task = compiler.getTask(output, fileManager, null, arguments, null, compilationUnits); + + boolean result = task.call(); + long compile_time = System.nanoTime() - t; + + if (!result) { // Java compilation failed + throw new LeekScriptException(Error.COMPILE_JAVA, output.toString()); + } + + t = System.nanoTime(); + ClassLoader classLoader = new ClassLoader() { + @Override + protected Class findClass(String name) throws ClassNotFoundException { + var bytes = fileManager.get(name).getCompiledBinaries(); + return defineClass(name, bytes, 0, bytes.length); + } + }; + + // Load inner classes before + for (var compiledClass : fileManager.getCompiled().values()) { + + if (useClassCache) { // Save bytecode + try { + var classFile = new FileOutputStream(Paths.get(IA_PATH, compiledClass.getName() + ".class").toFile()); + classFile.write(compiledClass.getCompiledBinaries()); + classFile.close(); + } catch (IOException e) { + throw new LeekScriptException(Error.CANNOT_WRITE_AI, e.getMessage()); + } } - - // On va compiler le java maintenant - JavaCompiler compiler = new JavaCompiler(java); - int status = JavaCompiler.INIT; - + + if (compiledClass.getName().equals(javaClassName)) continue; try { - compiler.compile(jar); - status = JavaCompiler.getStatus(); - } catch (CompilationException e) { - - error = e.getMessage(); - // ErrorManager.registerCompilationError(ai, e.getMessage()); - status = JavaCompiler.ERROR; - + classLoader.loadClass(compiledClass.getName()); } catch (Exception e) { - - // ErrorManager.exception(e, ai.getId()); - ErrorManager.exception(e); - status = JavaCompiler.ERROR; + throw new LeekScriptException(Error.CANNOT_LOAD_AI, e.getMessage()); } - if (status == JavaCompiler.ERROR) { - java.delete(); - throwException(error); + } + + // Load the main class + try { + var clazz = classLoader.loadClass(javaClassName); + var ai = (AI) clazz.getDeclaredConstructor().newInstance(); + long load_time = System.nanoTime() - t; + + ai.setId(file.getId()); + ai.setAnalyzeTime(analyze_time); + ai.setCompileTime(compile_time); + ai.setLoadTime(load_time); + ai.setLinesFile(lines); + + if (useClassCache) { + aiCache.put(javaClassName, new AIClassEntry(clazz, file.getTimestamp())); } + return ai; + } catch (Exception e) { + throw new LeekScriptException(Error.CANNOT_LOAD_AI, e.getMessage()); } - return IALoader.loadAI(IA_PATH, javaClassName); } public static void throwException(String error) throws LeekScriptException { @@ -177,12 +289,18 @@ public static void throwException(String error) throws LeekScriptException { if (lines.length >= 2 && lines[1].split(" ").length > 4) { String l = lines[1].split(" ")[2]; if (l.length() > 4 && !l.startsWith("runIA")) { - throw new LeekScriptException(LeekScriptException.CODE_TOO_LARGE_FUNCTION, l.substring(14, l.length() - 2)); + throw new LeekScriptException(Error.CODE_TOO_LARGE_FUNCTION, l.substring(14, l.length() - 2)); } } - throw new LeekScriptException(LeekScriptException.CODE_TOO_LARGE); + throw new LeekScriptException(Error.CODE_TOO_LARGE); } } - throw new LeekScriptException(LeekScriptException.CANT_COMPILE); + throw new LeekScriptException(Error.COMPILE_JAVA, error); } + + public static Resolver getFileSystemResolver() { + return fileSystemResolver; + } + + } \ No newline at end of file diff --git a/src/main/java/leekscript/compiler/LeekScriptException.java b/src/main/java/leekscript/compiler/LeekScriptException.java index 1fb49e96..fbc040dc 100644 --- a/src/main/java/leekscript/compiler/LeekScriptException.java +++ b/src/main/java/leekscript/compiler/LeekScriptException.java @@ -1,30 +1,29 @@ package leekscript.compiler; +import leekscript.common.Error; + public class LeekScriptException extends Exception { private static final long serialVersionUID = -5149423928011355230L; - public final static int CANT_COMPILE = 0; - public final static int CODE_TOO_LARGE = 1; - public final static int CODE_TOO_LARGE_FUNCTION = 2; - - private final int mType; - private String mFunction = null; + private final Error mType; + private String mMessage = null; - public LeekScriptException(int type) { + public LeekScriptException(Error type) { mType = type; } - public LeekScriptException(int type, String function) { + public LeekScriptException(Error type, String message) { mType = type; - mFunction = function; + mMessage = message; } - public int getType() { + public Error getType() { return mType; } - public String getFunction() { - return mFunction; + @Override + public String getMessage() { + return mType.name() + " : " + mMessage; } } diff --git a/src/main/java/leekscript/compiler/LineMapping.java b/src/main/java/leekscript/compiler/LineMapping.java new file mode 100644 index 00000000..9992457c --- /dev/null +++ b/src/main/java/leekscript/compiler/LineMapping.java @@ -0,0 +1,20 @@ +package leekscript.compiler; + +public class LineMapping { + + private final int mLeekScriptLine; + private final int mAI; + + public LineMapping(int leekScriptLine, int ai) { + mLeekScriptLine = leekScriptLine; + mAI = ai; + } + + public int getLeekScriptLine() { + return mLeekScriptLine; + } + + public int getAI() { + return mAI; + } +} diff --git a/src/main/java/leekscript/compiler/SimpleClassFile.java b/src/main/java/leekscript/compiler/SimpleClassFile.java new file mode 100644 index 00000000..112df267 --- /dev/null +++ b/src/main/java/leekscript/compiler/SimpleClassFile.java @@ -0,0 +1,32 @@ +package leekscript.compiler; + +import javax.tools.SimpleJavaFileObject; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; + +/** Holds compiled byte code in a byte array */ +public class SimpleClassFile extends SimpleJavaFileObject { + + private ByteArrayOutputStream out; + private String name; + + public SimpleClassFile(String name) { + super(URI.create(name), Kind.CLASS); + this.name = name; + } + + @Override + public OutputStream openOutputStream() throws IOException { + return out = new ByteArrayOutputStream(); + } + + public byte[] getCompiledBinaries() { + return out.toByteArray(); + } + + public String getName() { + return name; + } +} \ No newline at end of file diff --git a/src/main/java/leekscript/compiler/SimpleFileManager.java b/src/main/java/leekscript/compiler/SimpleFileManager.java new file mode 100644 index 00000000..257689b6 --- /dev/null +++ b/src/main/java/leekscript/compiler/SimpleFileManager.java @@ -0,0 +1,36 @@ +package leekscript.compiler; + +import java.util.HashMap; + +import javax.tools.FileObject; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; + +public class SimpleFileManager extends ForwardingJavaFileManager { + + private final HashMap compiled = new HashMap<>(); + + public SimpleFileManager(StandardJavaFileManager delegate) { + super(delegate); + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) { + var result = new SimpleClassFile(className); + compiled.put(className, result); + return result; + } + + public SimpleClassFile get(String name) { + return compiled.get(name); + } + + public void clear() { + compiled.clear(); + } + + public HashMap getCompiled() { + return compiled; + } +} \ No newline at end of file diff --git a/src/main/java/leekscript/compiler/SimpleSourceFile.java b/src/main/java/leekscript/compiler/SimpleSourceFile.java new file mode 100644 index 00000000..59067236 --- /dev/null +++ b/src/main/java/leekscript/compiler/SimpleSourceFile.java @@ -0,0 +1,20 @@ +package leekscript.compiler; + +import javax.tools.SimpleJavaFileObject; +import java.net.URI; + +/** Exposes given test source to the compiler. */ +public class SimpleSourceFile extends SimpleJavaFileObject { + + private final String content; + + public SimpleSourceFile(String qualifiedClassName, String testSource) { + super(URI.create(qualifiedClassName), Kind.SOURCE); + content = testSource; + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return content; + } +} \ No newline at end of file diff --git a/src/main/java/leekscript/compiler/WordCompiler.java b/src/main/java/leekscript/compiler/WordCompiler.java index a75a1af6..252074ff 100644 --- a/src/main/java/leekscript/compiler/WordCompiler.java +++ b/src/main/java/leekscript/compiler/WordCompiler.java @@ -1,7 +1,16 @@ package leekscript.compiler; +import java.util.HashSet; +import java.util.Set; +import java.util.TreeSet; + +import leekscript.common.AccessLevel; +import leekscript.common.Error; +import leekscript.common.Type; +import leekscript.compiler.AnalyzeError.AnalyzeErrorLevel; import leekscript.compiler.bloc.AbstractLeekBlock; import leekscript.compiler.bloc.AnonymousFunctionBlock; +import leekscript.compiler.bloc.ClassMethodBlock; import leekscript.compiler.bloc.ConditionalBloc; import leekscript.compiler.bloc.DoWhileBlock; import leekscript.compiler.bloc.ForBlock; @@ -11,94 +20,117 @@ import leekscript.compiler.bloc.MainLeekBlock; import leekscript.compiler.bloc.WhileBlock; import leekscript.compiler.exceptions.LeekCompilerException; -import leekscript.compiler.exceptions.LeekInstructionException; import leekscript.compiler.expression.AbstractExpression; import leekscript.compiler.expression.LeekAnonymousFunction; import leekscript.compiler.expression.LeekArray; import leekscript.compiler.expression.LeekBoolean; -import leekscript.compiler.expression.LeekConstant; import leekscript.compiler.expression.LeekExpression; import leekscript.compiler.expression.LeekExpressionException; import leekscript.compiler.expression.LeekExpressionFunction; -import leekscript.compiler.expression.LeekFunction; -import leekscript.compiler.expression.LeekFunctionValue; -import leekscript.compiler.expression.LeekGlobal; import leekscript.compiler.expression.LeekNull; import leekscript.compiler.expression.LeekNumber; +import leekscript.compiler.expression.LeekObject; import leekscript.compiler.expression.LeekParenthesis; import leekscript.compiler.expression.LeekString; import leekscript.compiler.expression.LeekVariable; import leekscript.compiler.expression.Operators; +import leekscript.compiler.expression.LeekVariable.VariableType; import leekscript.compiler.instruction.BlankInstruction; +import leekscript.compiler.instruction.ClassDeclarationInstruction; import leekscript.compiler.instruction.LeekBreakInstruction; import leekscript.compiler.instruction.LeekContinueInstruction; import leekscript.compiler.instruction.LeekExpressionInstruction; import leekscript.compiler.instruction.LeekGlobalDeclarationInstruction; import leekscript.compiler.instruction.LeekReturnInstruction; import leekscript.compiler.instruction.LeekVariableDeclarationInstruction; -import leekscript.runner.LeekConstants; -import leekscript.runner.LeekFunctions; public class WordCompiler { private final MainLeekBlock mMain; private AbstractLeekBlock mCurentBlock; + private AbstractLeekBlock mCurrentFunction; + private ClassDeclarationInstruction mCurrentClass; private final WordParser mCompiler; private int mLine; private AIFile mAI = null; + private Set errors = new TreeSet<>(); + private final int version; - public WordCompiler(WordParser cmp, MainLeekBlock main, AIFile ai) { + public WordCompiler(WordParser cmp, MainLeekBlock main, AIFile ai, int version) { mCompiler = cmp; mMain = main; mCurentBlock = main; + mCurrentFunction = main; mAI = ai; + this.version = version; } - public void readCode() throws Exception { + public void readCode() throws LeekCompilerException { try { - mCompiler.compile(); - // Receherche des fonctions utilisateur + mCompiler.compile(this); + // Recherche des fonctions utilisateur while (mCompiler.haveWords()) { - if (mCompiler.getWord().getWord().equals("global")) { + if (mCompiler.getWord().getType() == WordParser.T_STRING && mCompiler.getWord().getWord().equals("global")) { mCompiler.skipWord(); - String globalName = mCompiler.readWord().getWord(); - mMain.addGlobalDeclaration(globalName); - while (mCompiler.getWord().getType() == WordParser.T_VIRG && mCompiler.haveWords()) { + var global = mCompiler.readWord(); + // System.out.println("global = " + global.getWord() + " " + global.getLine()); + if (!isGlobalAvailable(global) || mMain.hasDeclaredGlobal(global.getWord())) { + addError(new AnalyzeError(global, AnalyzeErrorLevel.ERROR, Error.VARIABLE_NAME_UNAVAILABLE)); + } else { + mMain.addGlobal(global.getWord()); + } + if (mCompiler.getWord().getWord().equals("=")) { + mCompiler.skipWord(); + readExpression(); + } + while (mCompiler.haveWords() && mCompiler.getWord().getType() == WordParser.T_VIRG) { mCompiler.skipWord(); - globalName = mCompiler.readWord().getWord(); - mMain.addGlobalDeclaration(globalName); + global = mCompiler.readWord(); + if (!isGlobalAvailable(global) || mMain.hasDeclaredGlobal(global.getWord())) { + addError(new AnalyzeError(global, AnalyzeErrorLevel.ERROR, Error.VARIABLE_NAME_UNAVAILABLE)); + } else { + mMain.addGlobal(global.getWord()); + } + if (mCompiler.getWord().getWord().equals("=")) { + mCompiler.skipWord(); + readExpression(); + } } } else if (mCompiler.getWord().getWord().equals("function")) { mCompiler.skipWord(); - String funcName = mCompiler.readWord().getWord(); - if (funcName.equals("(")) + var funcName = mCompiler.readWord(); + if (funcName.getWord().equals("(")) continue; if (!isAvailable(funcName, false)) - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.FUNCTION_NAME_UNAVAILABLE); + throw new LeekCompilerException(mCompiler.getWord(), Error.FUNCTION_NAME_UNAVAILABLE); if (mCompiler.readWord().getType() != WordParser.T_PAR_LEFT) { - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.OPENING_PARENTHESIS_EXPECTED); + throw new LeekCompilerException(mCompiler.lastWord(), Error.OPENING_PARENTHESIS_EXPECTED); } int param_count = 0; + var parameters = new HashSet(); while (mCompiler.getWord().getType() != WordParser.T_PAR_RIGHT) { if (mCompiler.getWord().getType() == WordParser.T_OPERATOR && mCompiler.getWord().getWord().equals("@")) { mCompiler.skipWord(); } - if (mCompiler.getWord().getType() != WordParser.T_STRING) - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.PARAMETER_NAME_EXPECTED); - if (!isAvailable(mCompiler.getWord().getWord(), true)) - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.PARAMETER_NAME_UNAVAILABLE); - mCompiler.skipWord(); + if (mCompiler.getWord().getType() != WordParser.T_STRING) { + throw new LeekCompilerException(mCompiler.getWord(), Error.PARAMETER_NAME_EXPECTED); + } + var parameter = mCompiler.readWord(); + if (parameters.contains(parameter.getWord())) { + throw new LeekCompilerException(parameter, Error.PARAMETER_NAME_UNAVAILABLE); + } + parameters.add(parameter.getWord()); param_count++; if (mCompiler.getWord().getType() == WordParser.T_VIRG) mCompiler.skipWord(); } if (mCompiler.readWord().getType() != WordParser.T_PAR_RIGHT) { - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.PARENTHESIS_EXPECTED_AFTER_PARAMETERS); + throw new LeekCompilerException(mCompiler.getWord(), Error.PARENTHESIS_EXPECTED_AFTER_PARAMETERS); } - mMain.addFunctionDeclaration(funcName, param_count); + mMain.addFunctionDeclaration(funcName.getWord(), param_count); } else mCompiler.skipWord(); } @@ -108,7 +140,7 @@ public void readCode() throws Exception { // On vérifie les instructions en cours - if (mCurentBlock instanceof DoWhileBlock && !((DoWhileBlock) mCurentBlock).hasAccolade() && mCurentBlock.lastInstruction() != null) { + if (mCurentBlock instanceof DoWhileBlock && !((DoWhileBlock) mCurentBlock).hasAccolade() && mCurentBlock.isFull()) { DoWhileBlock do_block = (DoWhileBlock) mCurentBlock; mCurentBlock = mCurentBlock.endInstruction(); dowhileendBlock(do_block); @@ -119,7 +151,6 @@ public void readCode() throws Exception { break; // Puis on lit l'instruction - compileWord(); } while (mCurentBlock.getParent() != null && !mCurentBlock.hasAccolade()) { @@ -128,31 +159,44 @@ public void readCode() throws Exception { mCurentBlock = mCurentBlock.endInstruction(); dowhileendBlock(do_block); mCompiler.skipWord(); - } else + } else { + if (mCurentBlock.endInstruction() == mCurentBlock) { + throw new LeekCompilerException(mCompiler.lastWord(), Error.NO_BLOC_TO_CLOSE); + } mCurentBlock = mCurentBlock.endInstruction(); + } } if (!mMain.equals(mCurentBlock)) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.OPEN_BLOC_REMAINING); - } catch (LeekInstructionException e) { - throw new LeekCompilerException(mCompiler.lastWord(), e.getMessage()); + throw new LeekCompilerException(mCompiler.lastWord(), Error.OPEN_BLOC_REMAINING); + } catch (IndexOutOfBoundsException e) { - throw new LeekCompilerException(mCompiler.endWord(), LeekCompilerException.END_OF_SCRIPT_UNEXPECTED); + e.printStackTrace(System.out); + throw new LeekCompilerException(mCompiler.endWord(), Error.END_OF_SCRIPT_UNEXPECTED); } } - private void compileWord() throws Exception { + public void analyze() { + // Analyse sémantique + mCurentBlock = mMain; + mCurrentFunction = mMain; + mMain.analyze(this); + } + + private void compileWord() throws LeekCompilerException { mLine = mCompiler.getWord().getLine(); mMain.addInstruction(); IAWord word = mCompiler.getWord(); if (word.getType() == WordParser.T_END_INSTRUCTION) { - mCurentBlock.addInstruction(new BlankInstruction()); + // mCurentBlock.addInstruction(this, new BlankInstruction()); + mCurentBlock.setFull(true); mCompiler.skipWord(); return; } else if (word.getType() == WordParser.T_ACCOLADE_RIGHT) { // Fermeture de bloc - if (!mCurentBlock.hasAccolade() || mCurentBlock.getParent() == null) - throw new LeekCompilerException(word, LeekCompilerException.NO_BLOC_TO_CLOSE); - else { + if (!mCurentBlock.hasAccolade() || mCurentBlock.getParent() == null) { + // throw new LeekCompilerException(word, Error.NO_BLOC_TO_CLOSE); + errors.add(new AnalyzeError(word, AnalyzeErrorLevel.ERROR, Error.NO_BLOC_TO_CLOSE)); + } else { if (mCurentBlock instanceof DoWhileBlock) { DoWhileBlock do_block = (DoWhileBlock) mCurentBlock; mCurentBlock.checkEndBlock(); @@ -177,6 +221,11 @@ private void compileWord() throws Exception { mCompiler.skipWord(); globalDeclaration(); return; + } else if (version >= 2 && getCurrentBlock() instanceof MainLeekBlock && word.getWord().equals("class")) { + // Déclaration de classe + mCompiler.skipWord(); + classDeclaration(); + return; } else if (word.getWord().equals("if")) { mCompiler.skipWord(); ifBlock(); @@ -203,32 +252,35 @@ private void compileWord() throws Exception { return; } else if (word.getWord().equals("break")) { if (!mCurentBlock.isBreakable()) { - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.BREAK_OUT_OF_LOOP); + throw new LeekCompilerException(mCompiler.lastWord(), Error.BREAK_OUT_OF_LOOP); } mCompiler.skipWord(); - if (mCompiler.readWord().getType() != WordParser.T_END_INSTRUCTION) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.END_OF_INSTRUCTION_EXPECTED); - mCurentBlock.addInstruction(new LeekBreakInstruction(mCurentBlock.countInstructions(), mCompiler.lastWord().getLine(), mCompiler.lastWord().getAI())); + if (mCompiler.getWord().getType() == WordParser.T_END_INSTRUCTION) + mCompiler.skipWord(); + // throw new LeekCompilerException(mCompiler.lastWord(), Error.END_OF_INSTRUCTION_EXPECTED); + mCurentBlock.addInstruction(this, new LeekBreakInstruction(mCurentBlock.countInstructions(), mCompiler.lastWord().getLine(), mCompiler.lastWord().getAI())); return; } else if (word.getWord().equals("continue")) { if (!mCurentBlock.isBreakable()) { - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.CONTINUE_OUT_OF_LOOP); + throw new LeekCompilerException(mCompiler.lastWord(), Error.CONTINUE_OUT_OF_LOOP); } mCompiler.skipWord(); - if (mCompiler.readWord().getType() != WordParser.T_END_INSTRUCTION) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.END_OF_INSTRUCTION_EXPECTED); - mCurentBlock.addInstruction(new LeekContinueInstruction(mCurentBlock.countInstructions(), mLine, mAI)); + if (mCompiler.getWord().getType() == WordParser.T_END_INSTRUCTION) + mCompiler.skipWord(); + // if (mCompiler.readWord().getType() != WordParser.T_END_INSTRUCTION) + // throw new LeekCompilerException(mCompiler.lastWord(), Error.END_OF_INSTRUCTION_EXPECTED); + mCurentBlock.addInstruction(this, new LeekContinueInstruction(mCurentBlock.countInstructions(), mLine, mAI)); return; } else if (word.getWord().equals("return")) { mCompiler.skipWord(); AbstractExpression exp = null; if (mCompiler.getWord().getType() != WordParser.T_END_INSTRUCTION) { - exp = readExpression().getAbstractExpression(); + exp = readExpression(); } - if (mCompiler.readWord().getType() != WordParser.T_END_INSTRUCTION) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.END_OF_INSTRUCTION_EXPECTED); - mCurentBlock.addInstruction(new LeekReturnInstruction(mCurentBlock.countInstructions(), exp, mLine, mAI)); + if (mCompiler.haveWords() && mCompiler.getWord().getType() == WordParser.T_END_INSTRUCTION) + mCompiler.skipWord(); + mCurentBlock.addInstruction(this, new LeekReturnInstruction(mCurentBlock.countInstructions(), exp, mLine, mAI)); return; } else if (word.getWord().equals("function")) { mCompiler.skipWord(); @@ -237,89 +289,94 @@ private void compileWord() throws Exception { } } - AbstractExpression exp = readExpression().getAbstractExpression(); - if (mCompiler.readWord().getType() != WordParser.T_END_INSTRUCTION) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.END_OF_INSTRUCTION_EXPECTED); - mCurentBlock.addInstruction(new LeekExpressionInstruction(exp, mLine, mAI)); + AbstractExpression exp = readExpression(); + if (mCompiler.haveWords() && mCompiler.getWord().getType() == WordParser.T_END_INSTRUCTION) { + mCompiler.skipWord(); + } + // if (mCompiler.readWord().getType() != WordParser.T_END_INSTRUCTION) + // throw new LeekCompilerException(mCompiler.lastWord(), Error.END_OF_INSTRUCTION_EXPECTED); + mCurentBlock.addInstruction(this, new LeekExpressionInstruction(exp, mLine, mAI)); } public void writeJava(String className, JavaWriter writer, String AIClass) { mMain.writeJavaCode(writer, className, AIClass); } - private void includeBlock() throws Exception { + private void includeBlock() throws LeekCompilerException { // On vérifie qu'on est dans le bloc principal if (!mCurentBlock.equals(mMain)) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.INCLUDE_ONLY_IN_MAIN_BLOCK); + throw new LeekCompilerException(mCompiler.lastWord(), Error.INCLUDE_ONLY_IN_MAIN_BLOCK); // On récupere l'ia if (mCompiler.readWord().getType() != WordParser.T_PAR_LEFT) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.OPENING_PARENTHESIS_EXPECTED); + throw new LeekCompilerException(mCompiler.lastWord(), Error.OPENING_PARENTHESIS_EXPECTED); if (mCompiler.getWord().getType() != WordParser.T_VAR_STRING) - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.AI_NAME_EXPECTED); + throw new LeekCompilerException(mCompiler.getWord(), Error.AI_NAME_EXPECTED); String iaName = mCompiler.readWord().getWord(); - if (!mMain.includeAI(iaName)) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.AI_NOT_EXISTING, new String[] { iaName }); + if (!mMain.includeAI(this, iaName)) { + errors.add(new AnalyzeError(mCompiler.lastWord(), AnalyzeErrorLevel.ERROR, Error.AI_NOT_EXISTING, new String[] { iaName })); + } if (mCompiler.readWord().getType() != WordParser.T_PAR_RIGHT) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.CLOSING_PARENTHESIS_EXPECTED); - - if (mCompiler.readWord().getType() != WordParser.T_END_INSTRUCTION) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.END_OF_INSTRUCTION_EXPECTED); + throw new LeekCompilerException(mCompiler.lastWord(), Error.CLOSING_PARENTHESIS_EXPECTED); } private void functionBlock() throws LeekCompilerException { // Déclaration de fonction utilisateur if (!mCurentBlock.equals(mMain)) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.FUNCTION_ONLY_IN_MAIN_BLOCK); + throw new LeekCompilerException(mCompiler.lastWord(), Error.FUNCTION_ONLY_IN_MAIN_BLOCK); // Récupération du nom de la fonction if (mCompiler.getWord().getType() != WordParser.T_STRING) - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.FUNCTION_NAME_EXPECTED); - String funcName = mCompiler.readWord().getWord(); + throw new LeekCompilerException(mCompiler.getWord(), Error.FUNCTION_NAME_EXPECTED); + IAWord funcName = mCompiler.readWord(); if (!isAvailable(funcName, false)) - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.FUNCTION_NAME_UNAVAILABLE); + throw new LeekCompilerException(mCompiler.getWord(), Error.FUNCTION_NAME_UNAVAILABLE); if (mCompiler.readWord().getType() != WordParser.T_PAR_LEFT) { - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.OPENING_PARENTHESIS_EXPECTED); + throw new LeekCompilerException(mCompiler.lastWord(), Error.OPENING_PARENTHESIS_EXPECTED); } - FunctionBlock block = new FunctionBlock(mCurentBlock, mMain, mLine, mAI); - block.setName(funcName); + var previousFunction = mCurrentFunction; + FunctionBlock block = new FunctionBlock(mCurentBlock, mMain, mLine, mAI, funcName); mCurentBlock = block; + mCurrentFunction = block; while (mCompiler.getWord().getType() != WordParser.T_PAR_RIGHT) { boolean is_reference = false; if (mCompiler.getWord().getType() == WordParser.T_OPERATOR && mCompiler.getWord().getWord().equals("@")) { is_reference = true; + if (getVersion() >= 2) { + addError(new AnalyzeError(mCompiler.getWord(), AnalyzeErrorLevel.WARNING, Error.REFERENCE_DEPRECATED)); + } mCompiler.skipWord(); } if (mCompiler.getWord().getType() != WordParser.T_STRING) - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.PARAMETER_NAME_EXPECTED); - if (!isAvailable(mCompiler.getWord().getWord(), true)) - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.PARAMETER_NAME_UNAVAILABLE); - block.addParameter(mCompiler.readWord().getWord(), is_reference); + throw new LeekCompilerException(mCompiler.getWord(), Error.PARAMETER_NAME_EXPECTED); + // if (!isAvailable(mCompiler.getWord().getWord(), true)) + // throw new LeekCompilerException(mCompiler.getWord(), Error.PARAMETER_NAME_UNAVAILABLE); + block.addParameter(this, mCompiler.readWord(), is_reference); if (mCompiler.getWord().getType() == WordParser.T_VIRG) mCompiler.skipWord(); } if (mCompiler.readWord().getType() != WordParser.T_PAR_RIGHT) { - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.PARENTHESIS_EXPECTED_AFTER_PARAMETERS); + throw new LeekCompilerException(mCompiler.getWord(), Error.PARENTHESIS_EXPECTED_AFTER_PARAMETERS); } // On regarde s'il y a des accolades if (mCompiler.readWord().getType() != WordParser.T_ACCOLADE_LEFT) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.OPENING_CURLY_BRACKET_EXPECTED); + throw new LeekCompilerException(mCompiler.lastWord(), Error.OPENING_CURLY_BRACKET_EXPECTED); mMain.addFunction(block); + mCurrentFunction = previousFunction; } - private void forBlock() throws Exception { + private void forBlock() throws LeekCompilerException { // Bloc de type for(i=0;i<5;i++) ou encore for(element in tableau) // On peut déclarer une variable pendant l'instruction d'initialisation if (mCompiler.readWord().getType() != WordParser.T_PAR_LEFT) { - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.OPENING_PARENTHESIS_EXPECTED); + throw new LeekCompilerException(mCompiler.lastWord(), Error.OPENING_PARENTHESIS_EXPECTED); } boolean isDeclaration = false; - String varName = ""; AbstractLeekBlock forBlock = null; // Là on doit déterminer si y'a déclaration de variable @@ -327,113 +384,115 @@ private void forBlock() throws Exception { isDeclaration = true; mCompiler.skipWord(); } + // Référence ? + boolean reference1 = false; + if (mCompiler.getWord().getWord().equals("@")) { + reference1 = true; + if (getVersion() >= 2) { + addError(new AnalyzeError(mCompiler.getWord(), AnalyzeErrorLevel.WARNING, Error.REFERENCE_DEPRECATED)); + } + mCompiler.skipWord(); + } // On récupère ensuite le nom de la variable if (mCompiler.getWord().getType() != WordParser.T_STRING) - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.VARIABLE_NAME_EXPECTED); - varName = mCompiler.readWord().getWord(); - // Si c'est une déclaration on vérifie que le nom est disponnible - if (isDeclaration) { - if (!isAvailable(varName, true)) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.VARIABLE_NAME_UNAVAILABLE); - } else { - // Sinon on vérifie que la variable existe - if (!mCurentBlock.hasVariable(varName) && !mMain.hasGlobal(varName)) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.VARIABLE_NOT_EXISTS); - } - // Maintenant on va savoir si on a affaire à un for(i in array) ou à un - // for(i=0;i<... - if (mCompiler.getWord().getWord().equals(":")) {// C'est un - // for(key:value in - // array) + throw new LeekCompilerException(mCompiler.getWord(), Error.VARIABLE_NAME_EXPECTED); + IAWord varName = mCompiler.readWord(); + + // Maintenant on va savoir si on a affaire à un for (i in array) ou à un for(i=0;i<... + if (mCompiler.getWord().getWord().equals(":")) { // C'est un for (key:value in array) mCompiler.skipWord(); boolean isValueDeclaration = false; - if (mCompiler.getWord().getWord().equals("var")) {// Il y a - // déclaration - // de la valeur + if (mCompiler.getWord().getWord().equals("var")) { // Il y a déclaration de la valeur isValueDeclaration = true; mCompiler.skipWord(); } + // Référence ? + boolean reference2 = false; + if (mCompiler.getWord().getWord().equals("@")) { + reference2 = true; + if (getVersion() >= 2) { + addError(new AnalyzeError(mCompiler.getWord(), AnalyzeErrorLevel.WARNING, Error.REFERENCE_DEPRECATED)); + } + mCompiler.skipWord(); + } // On récupère ensuite le nom de la variable accueillant la valeur if (mCompiler.getWord().getType() != WordParser.T_STRING) - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.VARIABLE_NAME_EXPECTED); - String valueVarName = mCompiler.readWord().getWord(); - - if (isValueDeclaration) { - if (!isAvailable(valueVarName, true)) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.VARIABLE_NAME_UNAVAILABLE); - } else { - // Sinon on vérifie que la variable existe - if (!mCurentBlock.hasVariable(valueVarName) && !mMain.hasGlobal(valueVarName)) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.VARIABLE_NOT_EXISTS); - } + throw new LeekCompilerException(mCompiler.getWord(), Error.VARIABLE_NAME_EXPECTED); + IAWord valueVarName = mCompiler.readWord(); if (!mCompiler.readWord().getWord().equals("in")) - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.KEYWORD_IN_EXPECTED); + throw new LeekCompilerException(mCompiler.getWord(), Error.KEYWORD_IN_EXPECTED); // On déclare notre bloc foreach et on entre dedans - ForeachKeyBlock block = new ForeachKeyBlock(mCurentBlock, mMain, isDeclaration, isValueDeclaration, mLine, mAI); - mCurentBlock.addInstruction(block); + ForeachKeyBlock block = new ForeachKeyBlock(mCurentBlock, mMain, isDeclaration, isValueDeclaration, mLine, mAI, reference1, reference2); + mCurentBlock.addInstruction(this, block); mCurentBlock = block; // On lit le array (ou liste de valeurs) - AbstractExpression array = readExpression().getAbstractExpression(); + AbstractExpression array = readExpression(); block.setArray(array); - block.setKeyIterator(varName, isDeclaration); - block.setValueIterator(valueVarName, isValueDeclaration); + block.setKeyIterator(this, varName, isDeclaration); + block.setValueIterator(this, valueVarName, isValueDeclaration); forBlock = block; - } else if (mCompiler.getWord().getWord().equals("in")) {// C'est un - // for(i in - // array) + } else if (mCompiler.getWord().getWord().equals("in")) { // C'est un for (i in array) mCompiler.skipWord(); - ForeachBlock block = new ForeachBlock(mCurentBlock, mMain, isDeclaration, mLine, mAI); - mCurentBlock.addInstruction(block); + ForeachBlock block = new ForeachBlock(mCurentBlock, mMain, isDeclaration, mLine, mAI, reference1); + mCurentBlock.addInstruction(this, block); mCurentBlock = block; // On lit le array (ou liste de valeurs) - AbstractExpression array = readExpression().getAbstractExpression(); + AbstractExpression array = readExpression(); block.setArray(array); - block.setIterator(varName, isDeclaration); + block.setIterator(this, varName); forBlock = block; - } else if (mCompiler.getWord().getWord().equals("=")) {// C'est un - // for(i=0;i<1;i++) + } else if (mCompiler.getWord().getWord().equals("=")) { // C'est un for (i=0;i<1;i++) mCompiler.skipWord(); ForBlock block = new ForBlock(mCurentBlock, mMain, mLine, mAI); - mCurentBlock.addInstruction(block); + mCurentBlock.addInstruction(this, block); mCurentBlock = block; // On récupère la valeur de base du compteur - AbstractExpression initValue = readExpression().getAbstractExpression(); - if (isDeclaration) - block.addVariable(varName); - if (mCompiler.readWord().getType() != WordParser.T_END_INSTRUCTION) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.END_OF_INSTRUCTION_EXPECTED); - AbstractExpression condition = readExpression().getAbstractExpression(); - if (mCompiler.readWord().getType() != WordParser.T_END_INSTRUCTION) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.END_OF_INSTRUCTION_EXPECTED); - AbstractExpression incrementation = readExpression().getAbstractExpression(); - - // Attention si l'incrémentation n'est pas une expression Java fait - // la gueule ! + AbstractExpression initValue = readExpression(); + if (mCompiler.readWord().getType() != WordParser.T_END_INSTRUCTION) { + // errors.add(new AnalyzeError(mCompiler.getWord(), AnalyzeErrorLevel.ERROR, Error.END_OF_INSTRUCTION_EXPECTED)); + throw new LeekCompilerException(mCompiler.lastWord(), Error.END_OF_INSTRUCTION_EXPECTED); + // return; + } + // if (mCompiler.getWord().getType() == WordParser.T_END_INSTRUCTION) { + // mCompiler.skipWord(); + // } + AbstractExpression condition = readExpression(); + if (mCompiler.readWord().getType() != WordParser.T_END_INSTRUCTION) { + // errors.add(new AnalyzeError(mCompiler.getWord(), AnalyzeErrorLevel.ERROR, Error.END_OF_INSTRUCTION_EXPECTED)); + throw new LeekCompilerException(mCompiler.lastWord(), Error.END_OF_INSTRUCTION_EXPECTED); + // return; + } + // if (mCompiler.getWord().getType() == WordParser.T_END_INSTRUCTION) { + // mCompiler.skipWord(); + // } + AbstractExpression incrementation = readExpression(); + + // Attention si l'incrémentation n'est pas une expression Java fait la gueule ! if (incrementation != null && (incrementation instanceof LeekVariable || (incrementation instanceof LeekExpression && ((LeekExpression) incrementation).getOperator() == -1))) { - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.UNCOMPLETE_EXPRESSION); + throw new LeekCompilerException(mCompiler.lastWord(), Error.UNCOMPLETE_EXPRESSION); } - block.setInitialisation(varName, initValue, isDeclaration, block.hasGlobal(varName)); + block.setInitialisation(this, varName, initValue, isDeclaration, block.hasGlobal(varName.getWord())); block.setCondition(condition); block.setIncrementation(incrementation); forBlock = block; } else - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.KEYWORD_UNEXPECTED); + throw new LeekCompilerException(mCompiler.getWord(), Error.KEYWORD_UNEXPECTED); // On vérifie la parenthèse fermante if (mCompiler.readWord().getType() != WordParser.T_PAR_RIGHT) { - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.CLOSING_PARENTHESIS_EXPECTED); + throw new LeekCompilerException(mCompiler.lastWord(), Error.CLOSING_PARENTHESIS_EXPECTED); } // On regarde s'il y a des accolades if (mCompiler.getWord().getType() == WordParser.T_ACCOLADE_LEFT) { @@ -442,53 +501,58 @@ private void forBlock() throws Exception { forBlock.noAccolade(); } - private void whileBlock() throws Exception { + private void whileBlock() throws LeekCompilerException { if (mCompiler.readWord().getType() != WordParser.T_PAR_LEFT) { - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.OPENING_PARENTHESIS_EXPECTED); + throw new LeekCompilerException(mCompiler.lastWord(), Error.OPENING_PARENTHESIS_EXPECTED); } - AbstractExpression exp = readExpression().getAbstractExpression(); + AbstractExpression exp = readExpression(); if (mCompiler.readWord().getType() != WordParser.T_PAR_RIGHT) { - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.CLOSING_PARENTHESIS_EXPECTED); + throw new LeekCompilerException(mCompiler.lastWord(), Error.CLOSING_PARENTHESIS_EXPECTED); } WhileBlock bloc = new WhileBlock(mCurentBlock, mMain, mLine, mAI); bloc.setCondition(exp); - if (mCompiler.getWord().getType() == WordParser.T_ACCOLADE_LEFT) { + if (mCompiler.haveWords() && mCompiler.getWord().getType() == WordParser.T_ACCOLADE_LEFT) { mCompiler.skipWord(); - } else + } else if (mCompiler.getWord().getType() == WordParser.T_END_INSTRUCTION) { + mCompiler.skipWord(); + bloc.addInstruction(this, new BlankInstruction()); bloc.noAccolade(); - mCurentBlock.addInstruction(bloc); + } else { + bloc.noAccolade(); + } + mCurentBlock.addInstruction(this, bloc); mCurentBlock = bloc; } - private void dowhileBlock() throws LeekCompilerException, LeekInstructionException { + private void dowhileBlock() throws LeekCompilerException { DoWhileBlock bloc = new DoWhileBlock(mCurentBlock, mMain, mAI); if (mCompiler.getWord().getType() == WordParser.T_ACCOLADE_LEFT) { mCompiler.skipWord(); } else bloc.noAccolade(); - mCurentBlock.addInstruction(bloc); + mCurentBlock.addInstruction(this, bloc); mCurentBlock = bloc; } - private void dowhileendBlock(DoWhileBlock bloc) throws Exception { + private void dowhileendBlock(DoWhileBlock bloc) throws LeekCompilerException { if (!mCompiler.readWord().getWord().equals("while")) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.WHILE_EXPECTED_AFTER_DO); + throw new LeekCompilerException(mCompiler.lastWord(), Error.WHILE_EXPECTED_AFTER_DO); if (mCompiler.readWord().getType() != WordParser.T_PAR_LEFT) { - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.OPENING_PARENTHESIS_EXPECTED); + throw new LeekCompilerException(mCompiler.lastWord(), Error.OPENING_PARENTHESIS_EXPECTED); } - bloc.setCondition(readExpression().getAbstractExpression()); + bloc.setCondition(readExpression()); if (mCompiler.readWord().getType() != WordParser.T_PAR_RIGHT) { - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.CLOSING_PARENTHESIS_EXPECTED); + throw new LeekCompilerException(mCompiler.lastWord(), Error.CLOSING_PARENTHESIS_EXPECTED); } - if (mCompiler.getWord().getType() != WordParser.T_END_INSTRUCTION) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.END_OF_INSTRUCTION_EXPECTED); + // if (mCompiler.getWord().getType() != WordParser.T_END_INSTRUCTION) + // throw new LeekCompilerException(mCompiler.lastWord(), Error.END_OF_INSTRUCTION_EXPECTED); } - private void elseBlock() throws Exception { + private void elseBlock() throws LeekCompilerException { // On vérifie qu'on est bien associé à un bloc conditionnel ConditionalBloc last = mCurentBlock.getLastOpenedConditionalBlock(); if (last == null || last.getCondition() == null) { - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.NO_IF_BLOCK); + throw new LeekCompilerException(mCompiler.lastWord(), Error.NO_IF_BLOCK); } ConditionalBloc bloc = new ConditionalBloc(mCurentBlock, mMain, mLine, mAI); bloc.setParentCondition(last); @@ -496,11 +560,11 @@ private void elseBlock() throws Exception { // On veut un elseif mCompiler.skipWord(); if (mCompiler.readWord().getType() != WordParser.T_PAR_LEFT) { - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.OPENING_PARENTHESIS_EXPECTED); + throw new LeekCompilerException(mCompiler.lastWord(), Error.OPENING_PARENTHESIS_EXPECTED); } - AbstractExpression exp = readExpression().getAbstractExpression(); + AbstractExpression exp = readExpression(); if (mCompiler.readWord().getType() != WordParser.T_PAR_RIGHT) { - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.CLOSING_PARENTHESIS_EXPECTED); + throw new LeekCompilerException(mCompiler.lastWord(), Error.CLOSING_PARENTHESIS_EXPECTED); } bloc.setCondition(exp); } @@ -509,149 +573,344 @@ private void elseBlock() throws Exception { mCompiler.skipWord(); } else bloc.noAccolade(); - last.getParent().addInstruction(bloc); + last.getParent().addInstruction(this, bloc); mCurentBlock = bloc; } - private void ifBlock() throws Exception { + private void ifBlock() throws LeekCompilerException { if (mCompiler.readWord().getType() != WordParser.T_PAR_LEFT) { - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.OPENING_PARENTHESIS_EXPECTED); + throw new LeekCompilerException(mCompiler.lastWord(), Error.OPENING_PARENTHESIS_EXPECTED); } - AbstractExpression exp = readExpression().getAbstractExpression(); + AbstractExpression exp = readExpression(); if (mCompiler.readWord().getType() != WordParser.T_PAR_RIGHT) { - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.CLOSING_PARENTHESIS_EXPECTED); + throw new LeekCompilerException(mCompiler.lastWord(), Error.CLOSING_PARENTHESIS_EXPECTED); } ConditionalBloc bloc = new ConditionalBloc(mCurentBlock, mMain, mLine, mAI); bloc.setCondition(exp); if (mCompiler.getWord().getType() == WordParser.T_ACCOLADE_LEFT) { mCompiler.skipWord(); + } else if (mCompiler.getWord().getType() == WordParser.T_END_INSTRUCTION) { + mCompiler.skipWord(); + bloc.addInstruction(this, new BlankInstruction()); + bloc.noAccolade(); } else bloc.noAccolade(); - mCurentBlock.addInstruction(bloc); + mCurentBlock.addInstruction(this, bloc); mCurentBlock = bloc; } - private void globalDeclaration() throws Exception { + private void globalDeclaration() throws LeekCompilerException { // Il y a au moins une premiere variable IAWord word = mCompiler.readWord(); if (!(mCurentBlock instanceof MainLeekBlock)) - throw new LeekCompilerException(word, LeekCompilerException.GLOBAL_ONLY_IN_MAIN_BLOCK); + throw new LeekCompilerException(word, Error.GLOBAL_ONLY_IN_MAIN_BLOCK); if (word.getType() != WordParser.T_STRING) - throw new LeekCompilerException(word, LeekCompilerException.VAR_NAME_EXPECTED_AFTER_GLOBAL); - if (!isGlobalAvailable(word.getWord()) || mMain.hasDeclaredGlobal(word.getWord())) - throw new LeekCompilerException(word, LeekCompilerException.VARIABLE_NAME_UNAVAILABLE); - LeekGlobalDeclarationInstruction variable = new LeekGlobalDeclarationInstruction(word.getWord(), mLine, mAI); + throw new LeekCompilerException(word, Error.VAR_NAME_EXPECTED_AFTER_GLOBAL); + LeekGlobalDeclarationInstruction variable = new LeekGlobalDeclarationInstruction(word, mLine, mAI); // On regarde si une valeur est assignée if (mCompiler.getWord().getWord().equals("=")) { mCompiler.skipWord(); // Si oui on récupère la valeur en question - variable.setValue(readExpression().getAbstractExpression()); + variable.setValue(readExpression()); } // On ajoute la variable - mMain.addGlobal(variable.getName()); - mCurentBlock.addInstruction(variable); - while (mCompiler.getWord().getType() == WordParser.T_VIRG) { + mMain.addGlobalDeclaration(variable); + mCurentBlock.addInstruction(this, variable); + while (mCompiler.haveWords() && mCompiler.getWord().getType() == WordParser.T_VIRG) { // On regarde si y'en a d'autres mCompiler.skipWord();// On passe la virgule word = mCompiler.readWord(); if (word.getType() != WordParser.T_STRING) - throw new LeekCompilerException(word, LeekCompilerException.VAR_NAME_EXPECTED); - if (!isGlobalAvailable(word.getWord()) || mMain.hasDeclaredGlobal(word.getWord())) - throw new LeekCompilerException(word, LeekCompilerException.VARIABLE_NAME_UNAVAILABLE); - variable = new LeekGlobalDeclarationInstruction(word.getWord(), mLine, mAI); + throw new LeekCompilerException(word, Error.VAR_NAME_EXPECTED); + variable = new LeekGlobalDeclarationInstruction(word, mLine, mAI); // On regarde si une valeur est assign�e if (mCompiler.getWord().getWord().equals("=")) { mCompiler.skipWord(); // Si oui on récupère la valeur en question - variable.setValue(readExpression().getAbstractExpression()); + variable.setValue(readExpression()); } // On ajoute la variable - mMain.addGlobal(variable.getName()); - mCurentBlock.addInstruction(variable); + mMain.addGlobalDeclaration(variable); + mCurentBlock.addInstruction(this, variable); } - word = mCompiler.readWord(); - if (word.getType() != WordParser.T_END_INSTRUCTION) - throw new LeekCompilerException(word, LeekCompilerException.END_OF_INSTRUCTION_EXPECTED); + // word = mCompiler.readWord(); + // if (word.getType() != WordParser.T_END_INSTRUCTION) + // throw new LeekCompilerException(word, Error.END_OF_INSTRUCTION_EXPECTED); + if (mCompiler.haveWords() && mCompiler.getWord().getType() == WordParser.T_END_INSTRUCTION) + mCompiler.skipWord(); } - private void variableDeclaration() throws Exception { + private void variableDeclaration() throws LeekCompilerException { // Il y a au moins une premiere variable IAWord word = mCompiler.readWord(); - if (word.getType() != WordParser.T_STRING) - throw new LeekCompilerException(word, LeekCompilerException.VAR_NAME_EXPECTED); - if (!isAvailable(word.getWord(), true)) - throw new LeekCompilerException(word, LeekCompilerException.VARIABLE_NAME_UNAVAILABLE); - LeekVariableDeclarationInstruction variable = new LeekVariableDeclarationInstruction(word.getWord(), mLine, mAI); + if (word.getType() != WordParser.T_STRING) { + throw new LeekCompilerException(word, Error.VAR_NAME_EXPECTED); + } + if (getVersion() >= 3 && isKeyword(word)) { + addError(new AnalyzeError(word, AnalyzeErrorLevel.ERROR, Error.VARIABLE_NAME_UNAVAILABLE)); + } + LeekVariableDeclarationInstruction variable = new LeekVariableDeclarationInstruction(this, word, mLine, mAI, getCurrentFunction()); // On regarde si une valeur est assignée if (mCompiler.getWord().getWord().equals("=")) { mCompiler.skipWord(); // Si oui on récupère la valeur en question - mCurentBlock.setDeclaringVariable(variable.getName()); - variable.setValue(readExpression().getAbstractExpression()); - if (mCurentBlock.isDeclaringBariableUsed()) - variable.mustSepare(); - mCurentBlock.setDeclaringVariable(null); + variable.setValue(readExpression()); } - // On ajoute la variable - mCurentBlock.addVariable(variable.getName()); - mCurentBlock.addInstruction(variable); - while (mCompiler.getWord().getType() == WordParser.T_VIRG) { + mCurentBlock.addInstruction(this, variable); + while (mCompiler.haveWords() && mCompiler.getWord().getType() == WordParser.T_VIRG) { // On regarde si y'en a d'autres mCompiler.skipWord();// On passe la virgule word = mCompiler.readWord(); if (word.getType() != WordParser.T_STRING) - throw new LeekCompilerException(word, LeekCompilerException.VAR_NAME_EXPECTED); - if (!isAvailable(word.getWord(), true)) - throw new LeekCompilerException(word, LeekCompilerException.VARIABLE_NAME_UNAVAILABLE); - variable = new LeekVariableDeclarationInstruction(word.getWord(), mLine, mAI); - // On regarde si une valeur est assign�e + throw new LeekCompilerException(word, Error.VAR_NAME_EXPECTED); + if (getVersion() >= 3 && isKeyword(word)) { + addError(new AnalyzeError(word, AnalyzeErrorLevel.ERROR, Error.VARIABLE_NAME_UNAVAILABLE)); + } + variable = new LeekVariableDeclarationInstruction(this, word, mLine, mAI, getCurrentFunction()); + // On regarde si une valeur est assignée if (mCompiler.getWord().getWord().equals("=")) { mCompiler.skipWord(); // Si oui on récupère la valeur en question - variable.setValue(readExpression().getAbstractExpression()); + variable.setValue(readExpression()); } // On ajoute la variable - mCurentBlock.addVariable(variable.getName()); - mCurentBlock.addInstruction(variable); + mCurentBlock.addInstruction(this, variable); + } + if (mCompiler.haveWords() && mCompiler.getWord().getType() == WordParser.T_END_INSTRUCTION) { + mCompiler.skipWord(); + } + } + + public void classDeclaration() throws LeekCompilerException { + // Read class name + IAWord word = mCompiler.readWord(); + if (word.getType() != WordParser.T_STRING) { + throw new LeekCompilerException(word, Error.VAR_NAME_EXPECTED); } + if (mMain.hasUserClass(word.getWord())) { + throw new LeekCompilerException(word, Error.VARIABLE_NAME_UNAVAILABLE); + } + if (getVersion() >= 3 && isKeyword(word)) { + errors.add(new AnalyzeError(word, AnalyzeErrorLevel.ERROR, Error.VARIABLE_NAME_UNAVAILABLE, new String[] { word.getWord() })); + } + ClassDeclarationInstruction classDeclaration = new ClassDeclarationInstruction(word, mLine, mAI, false); + mMain.addClass(classDeclaration); + mCurrentClass = classDeclaration; + + if (mCompiler.getWord().getWord().equals("extends")) { + mCompiler.skipWord(); + IAWord parent = mCompiler.readWord(); + classDeclaration.setParent(parent); + } + if (mCompiler.getWord().getType() != WordParser.T_ACCOLADE_LEFT) { + throw new LeekCompilerException(mCompiler.getWord(), Error.OPENING_CURLY_BRACKET_EXPECTED); + } + mCompiler.skipWord(); + word = mCompiler.readWord(); - if (word.getType() != WordParser.T_END_INSTRUCTION) - throw new LeekCompilerException(word, LeekCompilerException.END_OF_INSTRUCTION_EXPECTED); + while (word.getType() != WordParser.T_ACCOLADE_RIGHT) { + switch (word.getWord()) { + case "public": + case "private": + case "protected": + { + AccessLevel level = AccessLevel.fromString(word.getWord()); + classAccessLevelMember(classDeclaration, level); + break; + } + case "constructor": { + classConstructor(classDeclaration, AccessLevel.PUBLIC); + break; + } + default: { + if (word.getType() == WordParser.T_STRING) { + mCompiler.back(); + classAccessLevelMember(classDeclaration, AccessLevel.PUBLIC); + } else { + throw new LeekCompilerException(word, Error.KEYWORD_UNEXPECTED); + } + } + } + word = mCompiler.readWord(); + } + if (word.getType() != WordParser.T_ACCOLADE_RIGHT) { + throw new LeekCompilerException(word, Error.END_OF_CLASS_EXPECTED); + } + mCurrentClass = null; + // mMain.addInstruction(this, classDeclaration); + } + + public void classAccessLevelMember(ClassDeclarationInstruction classDeclaration, AccessLevel accessLevel) throws LeekCompilerException { + IAWord name = mCompiler.readWord(); + switch (name.getWord()) { + case "constructor": + classConstructor(classDeclaration, accessLevel); + return; + case "static": + classStaticMember(classDeclaration, accessLevel); + return; + } + if (name.getWord().equals("class") || name.getWord().equals("super")) { + errors.add(new AnalyzeError(name, AnalyzeErrorLevel.ERROR, Error.RESERVED_FIELD, new String[] { name.getWord() })); + } else if (getVersion() >= 3 && isKeyword(name)) { + errors.add(new AnalyzeError(name, AnalyzeErrorLevel.ERROR, Error.VARIABLE_NAME_UNAVAILABLE, new String[] { name.getWord() })); + } + IAWord word2 = mCompiler.getWord(); + if (word2.getType() == WordParser.T_PAR_LEFT) { + // Méthode + ClassMethodBlock method = classMethod(classDeclaration, name.getWord(), false); + classDeclaration.addMethod(this, name, method, accessLevel); + } else { + // Field + AbstractExpression expr = null; + if (mCompiler.getWord().getType() == WordParser.T_OPERATOR && mCompiler.getWord().getWord().equals("=")) { + mCompiler.skipWord(); + expr = readExpression(); + } + classDeclaration.addField(this, name, expr, accessLevel); + if (mCompiler.getWord().getType() == WordParser.T_END_INSTRUCTION) + mCompiler.skipWord(); + } } - public LeekExpression readExpression() throws Exception { + public void classStaticMember(ClassDeclarationInstruction classDeclaration, AccessLevel accessLevel) throws LeekCompilerException { + IAWord name = mCompiler.readWord(); + + // Static field + AbstractExpression expr = null; + if (mCompiler.getWord().getType() == WordParser.T_OPERATOR && mCompiler.getWord().getWord().equals("=")) { + mCompiler.skipWord(); + expr = readExpression(); + } else if (mCompiler.getWord().getType() == WordParser.T_PAR_LEFT) { + // Méthode + ClassMethodBlock method = classMethod(classDeclaration, name.getWord(), true); + classDeclaration.addStaticMethod(this, name, method, accessLevel); + if (mCompiler.getWord().getType() == WordParser.T_END_INSTRUCTION) + mCompiler.skipWord(); + return; + } + if (name.getWord().equals("name") || name.getWord().equals("super") || name.getWord().equals("fields") || name.getWord().equals("staticFields") || name.getWord().equals("methods") || name.getWord().equals("staticMethods")) { + errors.add(new AnalyzeError(name, AnalyzeErrorLevel.ERROR, Error.RESERVED_FIELD, new String[] { name.getWord() })); + } else if (getVersion() >= 3 && isKeyword(name)) { + errors.add(new AnalyzeError(name, AnalyzeErrorLevel.ERROR, Error.VARIABLE_NAME_UNAVAILABLE, new String[] { name.getWord() })); + } + classDeclaration.addStaticField(name, expr, accessLevel); + + if (mCompiler.getWord().getType() == WordParser.T_END_INSTRUCTION) + mCompiler.skipWord(); + } + + public void classConstructor(ClassDeclarationInstruction classDeclaration, AccessLevel accessLevel) throws LeekCompilerException { + ClassMethodBlock constructor = classMethod(classDeclaration, "constructor", false); + classDeclaration.addConstructor(constructor, accessLevel); + } + + public ClassMethodBlock classMethod(ClassDeclarationInstruction classDeclaration, String name, boolean isStatic) throws LeekCompilerException { + + ClassMethodBlock method = new ClassMethodBlock(classDeclaration, isStatic, mCurentBlock, mMain, mLine, mAI); + + IAWord word = mCompiler.readWord(); + if (word.getType() != WordParser.T_PAR_LEFT) { + throw new LeekCompilerException(word, Error.OPENING_PARENTHESIS_EXPECTED); + } + int param_count = 0; + while (mCompiler.getWord().getType() != WordParser.T_PAR_RIGHT) { + if (mCompiler.getWord().getType() == WordParser.T_OPERATOR && mCompiler.getWord().getWord().equals("@")) { + errors.add(new AnalyzeError(mCompiler.getWord(), AnalyzeErrorLevel.WARNING, Error.REFERENCE_DEPRECATED)); + mCompiler.skipWord(); + } + if (mCompiler.getWord().getType() != WordParser.T_STRING) + throw new LeekCompilerException(mCompiler.getWord(), Error.PARAMETER_NAME_EXPECTED); + method.addParameter(this, mCompiler.getWord()); + mCompiler.skipWord(); + param_count++; + if (mCompiler.getWord().getType() == WordParser.T_VIRG) + mCompiler.skipWord(); + } + if (mCompiler.readWord().getType() != WordParser.T_PAR_RIGHT) { + throw new LeekCompilerException(mCompiler.getWord(), Error.PARENTHESIS_EXPECTED_AFTER_PARAMETERS); + } + if (classDeclaration.hasMethod(name, param_count)) { + throw new LeekCompilerException(mCompiler.getWord(), Error.CONSTRUCTOR_ALREADY_EXISTS); + } + + // On enregistre les block actuels + AbstractLeekBlock initialBlock = mCurentBlock; + int initialLine = mLine; + AIFile initialAI = mAI; + mCurentBlock = method; + + // Ouverture des accolades + if (mCompiler.readWord().getType() != WordParser.T_ACCOLADE_LEFT) + throw new LeekCompilerException(mCompiler.lastWord(), Error.OPENING_CURLY_BRACKET_EXPECTED); + + // Lecture du corps de la fonction + while (mCompiler.haveWords()) { + // Fermeture des blocs ouverts + if (mCurentBlock instanceof DoWhileBlock && !((DoWhileBlock) mCurentBlock).hasAccolade() && mCurentBlock.isFull()) { + DoWhileBlock do_block = (DoWhileBlock) mCurentBlock; + mCurentBlock = mCurentBlock.endInstruction(); + dowhileendBlock(do_block); + mCompiler.skipWord(); + } else + mCurentBlock = mCurentBlock.endInstruction(); + if (!mCompiler.haveWords()) + break; + + // On regarde si on veut fermer la fonction anonyme + if (mCompiler.getWord().getType() == WordParser.T_ACCOLADE_RIGHT && mCurentBlock == method) { + mCompiler.skipWord(); + break; // Fermeture de la fonction anonyme + } else + compileWord(); + } + // On remet le bloc initial + mCurentBlock = initialBlock; + mLine = initialLine; + mAI = initialAI; + return method; + } + + public AbstractExpression readExpression() throws LeekCompilerException { LeekExpression retour = new LeekExpression(); while (mCompiler.haveWords()) { IAWord word = mCompiler.getWord(); + if (word.getType() == WordParser.T_PAR_RIGHT || word.getType() == WordParser.T_ACCOLADE_RIGHT || word.getType() == WordParser.T_END_INSTRUCTION) { + break; + } if (retour.needOperator()) { // Si on attend un opérateur mais qu'il vient pas if (word.getType() == WordParser.T_BRACKET_LEFT) { - mCompiler.skipWord();// On avance le curseur pour être au - // début de l'expression + mCompiler.skipWord(); // On avance le curseur pour être au début de l'expression - LeekExpression exp = readExpression(); + AbstractExpression exp = readExpression(); if (mCompiler.getWord().getType() != WordParser.T_BRACKET_RIGHT) { - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.CLOSING_SQUARE_BRACKET_EXPECTED); + throw new LeekCompilerException(mCompiler.getWord(), Error.CLOSING_SQUARE_BRACKET_EXPECTED); } - retour.addBracket(exp.getAbstractExpression()); + retour.addBracket(exp); } else if (word.getType() == WordParser.T_PAR_LEFT) { - mCompiler.skipWord();// On avance le curseur pour être au - // début de l'expression - LeekExpressionFunction function = new LeekExpressionFunction(); + + LeekExpressionFunction function = new LeekExpressionFunction(word); + mCompiler.skipWord();// On avance le curseur pour être au début de l'expression while (mCompiler.getWord().getType() != WordParser.T_PAR_RIGHT) { - function.addParameter(readExpression().getAbstractExpression()); + function.addParameter(readExpression()); if (mCompiler.getWord().getType() == WordParser.T_VIRG) mCompiler.skipWord(); } - if (mCompiler.getWord().getType() != WordParser.T_PAR_RIGHT) { - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.PARENTHESIS_EXPECTED_AFTER_PARAMETERS); + if (mCompiler.haveWords() && mCompiler.getWord().getType() != WordParser.T_PAR_RIGHT) { + throw new LeekCompilerException(mCompiler.getWord(), Error.PARENTHESIS_EXPECTED_AFTER_PARAMETERS); } - retour.addFunction(function); + } else if (word.getType() == WordParser.T_DOT) { + // Object access + mCompiler.skipWord(); + IAWord name = mCompiler.getWord(); + retour.addObjectAccess(name); + } else if (word.getType() == WordParser.T_OPERATOR) { - int operator = Operators.getOperator(word.getWord()); + int operator = Operators.getOperator(word.getWord(), getVersion()); // Là c'est soit un opérateur (+ - ...) soit un suffix // unaire (++ -- ) sinon on sort de l'expression @@ -661,18 +920,18 @@ public LeekExpression readExpression() throws Exception { break; if (Operators.isUnarySuffix(operator)) - retour.addUnarySuffix(operator); + retour.addUnarySuffix(operator, word); else - retour.addOperator(operator); + retour.addOperator(operator, word); } else if (word.getType() == WordParser.T_STRING) { if (word.getWord().equals("is")) { mCompiler.skipWord(); word = mCompiler.getWord(); if (word.getWord().equals("not")) { - mCompiler.skipWord(); - retour.addOperator(Operators.NOTEQUALS); + IAWord token = mCompiler.readWord(); + retour.addOperator(Operators.NOTEQUALS, token); } else { - retour.addOperator(Operators.EQUALS); + retour.addOperator(Operators.EQUALS, word); } continue; } @@ -680,9 +939,16 @@ public LeekExpression readExpression() throws Exception { } else break; } else { - if (word.getType() == WordParser.T_NUMBER) - retour.addExpression(new LeekNumber(Double.parseDouble(word.getWord()))); - else if (word.getType() == WordParser.T_VAR_STRING) { + if (word.getType() == WordParser.T_NUMBER) { + var s = word.getWord(); + var type = s.contains(".") ? Type.REAL : Type.INT; + try { + Integer.parseInt(s); + } catch (NumberFormatException e) { + type = Type.REAL; + } + retour.addExpression(new LeekNumber(Double.parseDouble(word.getWord()), type)); + } else if (word.getType() == WordParser.T_VAR_STRING) { retour.addExpression(new LeekString(word.getWord())); } else if (word.getType() == WordParser.T_BRACKET_LEFT) { // Déclaration d'un tableau @@ -691,86 +957,103 @@ else if (word.getType() == WordParser.T_VAR_STRING) { int type = 0;// 0 => A déterminer, 1=> Simple, 2 => // Clé:valeur while (mCompiler.getWord().getType() != WordParser.T_BRACKET_RIGHT) { - AbstractExpression exp = readExpression().getAbstractExpression(); + AbstractExpression exp = readExpression(); if (mCompiler.getWord().getWord().equals(":")) { if (type == 0) type = 2; else if (type == 1) - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.SIMPLE_ARRAY); + throw new LeekCompilerException(mCompiler.getWord(), Error.SIMPLE_ARRAY); mCompiler.skipWord(); - AbstractExpression value = readExpression().getAbstractExpression(); + AbstractExpression value = readExpression(); array.addValue(exp, value); } else { if (type == 0) type = 1; else if (type == 2) - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.ASSOCIATIVE_ARRAY); + throw new LeekCompilerException(mCompiler.getWord(), Error.ASSOCIATIVE_ARRAY); array.addValue(exp); } if (mCompiler.getWord().getType() == WordParser.T_VIRG) mCompiler.skipWord(); } if (mCompiler.getWord().getType() != WordParser.T_BRACKET_RIGHT) { - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.PARENTHESIS_EXPECTED_AFTER_PARAMETERS); + throw new LeekCompilerException(mCompiler.getWord(), Error.PARENTHESIS_EXPECTED_AFTER_PARAMETERS); } retour.addExpression(array); - } else if (word.getType() == WordParser.T_STRING) { - if (mCurentBlock.hasVariable(word.getWord())) - retour.addExpression(new LeekVariable(word.getWord())); - else if (mMain.hasGlobal(word.getWord())) - retour.addExpression(new LeekGlobal(word.getWord())); - else if (LeekFunctions.isFunction(word.getWord()) > -1 || mMain.hasUserFunction(word.getWord(), true)) { - LeekFunction function = new LeekFunction(word.getWord()); - String fname = word.getWord(); - // On doit lire la fonction + + } else if (getVersion() >= 2 && word.getType() == WordParser.T_ACCOLADE_LEFT) { + + // Déclaration d'un objet + mCompiler.skipWord(); + var object = new LeekObject(); + + while (mCompiler.getWord().getType() != WordParser.T_ACCOLADE_RIGHT) { + if (mCompiler.getWord().getType() != WordParser.T_STRING) { + throw new LeekCompilerException(mCompiler.getWord(), Error.PARENTHESIS_EXPECTED_AFTER_PARAMETERS); + } + String key = mCompiler.getWord().getWord(); mCompiler.skipWord(); - if (mCompiler.getWord().getType() != WordParser.T_PAR_LEFT) { - // On utilise le nom de la fonction comme une - // variable - String namespace = LeekFunctions.getNamespace(fname); - retour.addExpression(new LeekFunctionValue(fname, namespace)); - continue; - } else { + + if (!mCompiler.getWord().getWord().equals(":")) { + throw new LeekCompilerException(mCompiler.getWord(), Error.PARENTHESIS_EXPECTED_AFTER_PARAMETERS); + } + mCompiler.skipWord(); + + AbstractExpression value = readExpression(); + object.addEntry(key, value); + + if (mCompiler.getWord().getType() == WordParser.T_VIRG) { mCompiler.skipWord(); - while (mCompiler.getWord().getType() != WordParser.T_PAR_RIGHT) { - function.addParameter(readExpression().getAbstractExpression()); - if (mCompiler.getWord().getType() == WordParser.T_VIRG) - mCompiler.skipWord(); - } - if (mCompiler.getWord().getType() != WordParser.T_PAR_RIGHT) { - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.PARENTHESIS_EXPECTED_AFTER_PARAMETERS); - } - retour.addExpression(function); } - } else if (word.getWord().equalsIgnoreCase("function")) { - retour.addExpression(readAnonymousFunction()); + } + if (mCompiler.getWord().getType() != WordParser.T_ACCOLADE_RIGHT) { + throw new LeekCompilerException(mCompiler.getWord(), Error.PARENTHESIS_EXPECTED_AFTER_PARAMETERS); + } + retour.addExpression(object); + + } else if (word.getType() == WordParser.T_STRING) { - } else if (word.getWord().equalsIgnoreCase("true")) + if (mMain.hasGlobal(word.getWord())) { + retour.addExpression(new LeekVariable(this, word, VariableType.GLOBAL)); + } else if (wordEquals(word, "function")) { + retour.addExpression(readAnonymousFunction()); + } else if (wordEquals(word, "true")) retour.addExpression(new LeekBoolean(true)); - else if (word.getWord().equalsIgnoreCase("false")) + else if (wordEquals(word, "false")) retour.addExpression(new LeekBoolean(false)); - else if (word.getWord().equalsIgnoreCase("null")) + else if (wordEquals(word, "null")) retour.addExpression(new LeekNull()); - else if (word.getWord().equalsIgnoreCase("not")) - retour.addUnaryPrefix(Operators.NOT); - else if (LeekConstants.get(word.getWord()) != null) - retour.addExpression(new LeekConstant(word.getWord(), LeekConstants.get(word.getWord()))); - else { - throw new LeekCompilerException(word, LeekCompilerException.UNKNOWN_VARIABLE_OR_FUNCTION); + else if (wordEquals(word, "not")) + retour.addUnaryPrefix(Operators.NOT, word); + else if (getVersion() >= 2 && word.getWord().equals("new")) { + retour.addUnaryPrefix(Operators.NEW, word); + } else if (getVersion() >= 2 && word.getWord().equals("super")) { + // super doit être dans une méthode + if (!(mCurentBlock instanceof ClassMethodBlock)) { + errors.add(new AnalyzeError(word, AnalyzeErrorLevel.ERROR, Error.KEYWORD_MUST_BE_IN_CLASS)); + retour.addExpression(new LeekVariable(this, word, VariableType.LOCAL)); + } else { + if (((ClassMethodBlock) mCurentBlock).getClassDeclaration().getParentToken() == null) { + errors.add(new AnalyzeError(word, AnalyzeErrorLevel.ERROR, Error.SUPER_NOT_AVAILABLE_PARENT)); + } + retour.addExpression(new LeekVariable(word, VariableType.SUPER, ((ClassMethodBlock) mCurentBlock).getClassDeclaration())); + } + } else { + retour.addExpression(new LeekVariable(this, word, VariableType.LOCAL)); + // throw new LeekCompilerException(word, Error.UNKNOWN_VARIABLE_OR_FUNCTION); } } else if (word.getType() == WordParser.T_PAR_LEFT) { - mCompiler.skipWord();// On avance le curseur pour bien être - // au début de l'expression + mCompiler.skipWord(); // On avance le curseur pour bien être au début de l'expression - LeekExpression exp = readExpression(); - if (mCompiler.getWord().getType() != WordParser.T_PAR_RIGHT) { - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.CLOSING_PARENTHESIS_EXPECTED); + AbstractExpression exp = readExpression(); + if (mCompiler.haveWords() && mCompiler.getWord().getType() != WordParser.T_PAR_RIGHT) { + throw new LeekCompilerException(mCompiler.getWord(), Error.CLOSING_PARENTHESIS_EXPECTED); } - retour.addExpression(new LeekParenthesis(exp.getAbstractExpression())); + retour.addExpression(new LeekParenthesis(exp)); } else if (word.getType() == WordParser.T_OPERATOR) { // Si c'est un opérateur (il doit forcément être unaire et // de type préfix (! )) - int operator = Operators.getOperator(word.getWord()); + int operator = Operators.getOperator(word.getWord(), getVersion()); if (operator == Operators.MINUS) operator = Operators.UNARY_MINUS; else if (operator == Operators.DECREMENT) @@ -780,67 +1063,98 @@ else if (operator == Operators.INCREMENT) if (Operators.isUnaryPrefix(operator)) { // Si oui on l'ajoute - retour.addUnaryPrefix(operator); + retour.addUnaryPrefix(operator, word); } else { - throw new LeekCompilerException(word, LeekCompilerException.OPERATOR_UNEXPECTED); + errors.add(new AnalyzeError(word, AnalyzeErrorLevel.ERROR, Error.OPERATOR_UNEXPECTED)); + // throw new LeekCompilerException(word, Error.OPERATOR_UNEXPECTED); } } else { - throw new LeekCompilerException(word, LeekCompilerException.VALUE_EXPECTED); + errors.add(new AnalyzeError(word, AnalyzeErrorLevel.ERROR, Error.VALUE_EXPECTED)); + // throw new LeekCompilerException(word, Error.VALUE_EXPECTED); } } mCompiler.skipWord(); } // Avant de retourner, on valide l'expression + AbstractExpression result = retour; + if (retour.getOperator() == -1) { + result = retour.getExpression1(); + } + if (getVersion() == 1 && result instanceof LeekExpression) { + var expr = (LeekExpression) result; + if (expr.getOperator() == Operators.NOT && expr.getExpression2() == null) { + // Un "not" tout seul est valide en LS 1.0 + result = new LeekVariable(this, expr.getOperatorToken(), VariableType.LOCAL); + } + } + if (result == null) { + throw new LeekCompilerException(mCompiler.lastWord(), Error.UNCOMPLETE_EXPRESSION); + } try { - retour.getAbstractExpression().validExpression(mMain); + result.validExpression(this, mMain); } catch (LeekExpressionException e) { - throw new LeekCompilerException(mCompiler.lastWord(), e.getMessage(), new String[] { e.getExpression() }); + throw new LeekCompilerException(mCompiler.lastWord(), e.getError(), new String[] { e.getExpression() }); } - return retour; + return result; } - private LeekAnonymousFunction readAnonymousFunction() throws Exception { + private boolean wordEquals(IAWord word, String expected) { + if (getVersion() <= 2) { + return word.getWord().equalsIgnoreCase(expected); + } + return word.getWord().equals(expected); + } + + private LeekAnonymousFunction readAnonymousFunction() throws LeekCompilerException { mCompiler.skipWord(); if (mCompiler.readWord().getType() != WordParser.T_PAR_LEFT) { - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.PARENTHESIS_EXPECTED_AFTER_FUNCTION); + throw new LeekCompilerException(mCompiler.getWord(), Error.PARENTHESIS_EXPECTED_AFTER_FUNCTION); } // On enregistre les block actuels AbstractLeekBlock initialBlock = mCurentBlock; + var previousFunction = mCurrentFunction; int initialLine = mLine; AIFile initialAI = mAI; AnonymousFunctionBlock block = new AnonymousFunctionBlock(mCurentBlock, mMain, mLine, mAI); - if (initialBlock.getDeclaringVariable() != null) - block.addVariable(initialBlock.getDeclaringVariable()); + // if (initialBlock.getDeclaringVariable() != null) + // block.addVariable(new LeekVariable(initialBlock.getDeclaringVariable(), VariableType.LOCAL)); mCurentBlock = block; + mCurrentFunction = block; // Lecture des paramètres while (mCompiler.getWord().getType() != WordParser.T_PAR_RIGHT) { boolean is_reference = false; if (mCompiler.getWord().getType() == WordParser.T_OPERATOR && mCompiler.getWord().getWord().equals("@")) { is_reference = true; + if (getVersion() >= 2) { + addError(new AnalyzeError(mCompiler.getWord(), AnalyzeErrorLevel.WARNING, Error.REFERENCE_DEPRECATED)); + } mCompiler.skipWord(); } - if (mCompiler.getWord().getType() != WordParser.T_STRING) - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.PARAMETER_NAME_EXPECTED); - if (!isAvailable(mCompiler.getWord().getWord(), true)) - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.PARAMETER_NAME_UNAVAILABLE); - block.addParameter(mCompiler.readWord().getWord(), is_reference); + if (mCompiler.getWord().getType() != WordParser.T_STRING) { + throw new LeekCompilerException(mCompiler.getWord(), Error.PARAMETER_NAME_EXPECTED); + } + var parameter = mCompiler.readWord(); + if (block.hasParameter(parameter.getWord())) { + throw new LeekCompilerException(parameter, Error.PARAMETER_NAME_UNAVAILABLE); + } + block.addParameter(this, parameter, is_reference); if (mCompiler.getWord().getType() == WordParser.T_VIRG) mCompiler.skipWord(); } if (mCompiler.readWord().getType() != WordParser.T_PAR_RIGHT) { - throw new LeekCompilerException(mCompiler.getWord(), LeekCompilerException.PARENTHESIS_EXPECTED_AFTER_PARAMETERS); + throw new LeekCompilerException(mCompiler.getWord(), Error.PARENTHESIS_EXPECTED_AFTER_PARAMETERS); } // Ouverture des accolades if (mCompiler.readWord().getType() != WordParser.T_ACCOLADE_LEFT) - throw new LeekCompilerException(mCompiler.lastWord(), LeekCompilerException.OPENING_CURLY_BRACKET_EXPECTED); + throw new LeekCompilerException(mCompiler.lastWord(), Error.OPENING_CURLY_BRACKET_EXPECTED); // Lecture du corp de la fonction while (mCompiler.haveWords()) { // Fermeture des blocs ouverts - if (mCurentBlock instanceof DoWhileBlock && !((DoWhileBlock) mCurentBlock).hasAccolade() && mCurentBlock.lastInstruction() != null) { + if (mCurentBlock instanceof DoWhileBlock && !((DoWhileBlock) mCurentBlock).hasAccolade() && mCurentBlock.isFull()) { DoWhileBlock do_block = (DoWhileBlock) mCurentBlock; mCurentBlock = mCurentBlock.endInstruction(); dowhileendBlock(do_block); @@ -864,31 +1178,37 @@ private LeekAnonymousFunction readAnonymousFunction() throws Exception { mCurentBlock = initialBlock; mLine = initialLine; mAI = initialAI; + mCurrentFunction = previousFunction; return new LeekAnonymousFunction(block); } - public boolean isAvailable(String word, boolean allFunctions) { - if (word.equalsIgnoreCase("in") || word.equalsIgnoreCase("global") || word.equalsIgnoreCase("var") || word.equalsIgnoreCase("for") || word.equalsIgnoreCase("else") - || word.equalsIgnoreCase("if") || word.equalsIgnoreCase("break") || word.equalsIgnoreCase("return") || word.equalsIgnoreCase("do") || word.equalsIgnoreCase("while") - || word.equalsIgnoreCase("function") || word.equalsIgnoreCase("true") || word.equalsIgnoreCase("false") || word.equalsIgnoreCase("null")) - return false; + public boolean isKeyword(IAWord word) { + for (var w : WordParser.reservedWords) { + if (wordEquals(word, w)) return true; + } + return false; + } + + public boolean isAvailable(IAWord word, boolean allFunctions) { + if (getVersion() >= 3 && isKeyword(word)) return false; // if(LeekFunctions.isFunction(word) >= 0 || mMain.hasGlobal(word) || // mMain.hasUserFunction(word, allFunctions) || // mCurentBlock.hasVariable(word)) return false; - if (mMain.hasGlobal(word) || mMain.hasUserFunction(word, allFunctions) || mCurentBlock.hasVariable(word)) + if (mMain.hasGlobal(word.getWord()) || mMain.hasUserFunction(word.getWord(), allFunctions) || mCurentBlock.hasVariable(word.getWord())) return false; return true; } - public boolean isGlobalAvailable(String word) { - if (word.equalsIgnoreCase("in") || word.equalsIgnoreCase("global") || word.equalsIgnoreCase("var") || word.equalsIgnoreCase("for") || word.equalsIgnoreCase("else") - || word.equalsIgnoreCase("if") || word.equalsIgnoreCase("break") || word.equalsIgnoreCase("return") || word.equalsIgnoreCase("do") || word.equalsIgnoreCase("while") - || word.equalsIgnoreCase("function") || word.equalsIgnoreCase("true") || word.equalsIgnoreCase("false") || word.equalsIgnoreCase("null")) - return false; + public boolean isGlobalAvailable(IAWord word) { + if (getVersion() <= 2) { + if (word.getWord().equalsIgnoreCase("in") || word.getWord().equalsIgnoreCase("global") || word.getWord().equalsIgnoreCase("var") || word.getWord().equalsIgnoreCase("for") || word.getWord().equalsIgnoreCase("else") || word.getWord().equalsIgnoreCase("if") || word.getWord().equalsIgnoreCase("break") || word.getWord().equalsIgnoreCase("return") || word.getWord().equalsIgnoreCase("do") || word.getWord().equalsIgnoreCase("while") || word.getWord().equalsIgnoreCase("function") || word.getWord().equalsIgnoreCase("true") || word.getWord().equalsIgnoreCase("false") || word.getWord().equalsIgnoreCase("null")) + return false; + } + if (getVersion() >= 3 && isKeyword(word)) return false; // if(LeekFunctions.isFunction(word) >= 0 || mMain.hasUserFunction(word, // false) || mCurentBlock.hasVariable(word)) return false; - if (mMain.hasUserFunction(word, false) || mCurentBlock.hasVariable(word)) + if (mMain.hasUserFunction(word.getWord(), false) || mCurentBlock.hasVariable(word.getWord())) return false; return true; } @@ -896,4 +1216,56 @@ public boolean isGlobalAvailable(String word) { public String getString() { return mMain.getCode(); } + + public Set getErrors() { + return this.errors; + } + + public AbstractLeekBlock getCurrentBlock() { + return mCurentBlock; + } + + public AbstractLeekBlock getCurrentFunction() { + return mCurrentFunction; + } + + public void addError(AnalyzeError error) { + this.errors.add(error); + } + + public void setCurrentBlock(AbstractLeekBlock block) { + mCurentBlock = block; + } + + public WordParser getParser() { + return mCompiler; + } + + public MainLeekBlock getMainBlock() { + return mMain; + } + + public int getVersion() { + return this.version; + } + + public void addErrors(Set errors) { + this.errors.addAll(errors); + } + + public ClassDeclarationInstruction getCurrentClass() { + return mCurrentClass; + } + + public void setCurrentClass(ClassDeclarationInstruction clazz) { + this.mCurrentClass = clazz; + } + + public void setCurrentFunction(AbstractLeekBlock block) { + this.mCurrentFunction = block; + } + + public AIFile getAI() { + return mAI; + } } diff --git a/src/main/java/leekscript/compiler/WordParser.java b/src/main/java/leekscript/compiler/WordParser.java index 9fafa314..817c3d9a 100644 --- a/src/main/java/leekscript/compiler/WordParser.java +++ b/src/main/java/leekscript/compiler/WordParser.java @@ -2,19 +2,32 @@ import java.util.ArrayList; +import leekscript.compiler.AnalyzeError.AnalyzeErrorLevel; import leekscript.compiler.exceptions.LeekCompilerException; +import leekscript.common.Error; +/** + * Son but est de convertir le LeekCode en un "bytecode" plus rapide à + * exécuter Il doit aussi trouver les erreurs dans le LeekCode + */ public class WordParser { - /** - * Son but est de convertir le LeekCode en un "bytecode" plus rapide à - * éxécuter Il doit aussi trouver les erreurs dans le LeekCode - */ + + public static final String[] reservedWords = new String[] { + "abstract", "arguments", "await", "break", "byte", "case", "catch", + "char", "class", "const", "constructor", "continue", "default", "do", "double", "else", "enum", "eval", + "export", "extends", "false", "final", "finally", "float", "for", "function", + "goto", "if", "implements", "import", "in", "instanceof", "int", "interface", + "let", "long", "native", "new", "null", "package", "private", "protected", + "public", "return", "short", "static", "super", "switch", "synchronized", "this", + "throw", "throws", "transient", "true", "try", "typeof", "var", "void", + "volatile", "while", "with", "yield" + }; /** * Instructions byte(0) (0-255) byte => instruction - * + * * 1 : Etiquette SmallInt (2) : numéro d'étiquette - * + * * 2 : SetVariable StringUTF : nom variable */ @@ -28,7 +41,6 @@ public class WordParser { public final static int T_PAR_RIGHT = 7; public final static int T_VIRG = 8; public final static int T_CONDITION_OPERATOR = 9; - public final static int T_DOUBLE_POINT = 14; public final static int T_ACCOLADE_LEFT = 10; public final static int T_ACCOLADE_RIGHT = 11; @@ -36,6 +48,9 @@ public class WordParser { public final static int T_BRACKET_LEFT = 12; public final static int T_BRACKET_RIGHT = 13; + public final static int T_DOUBLE_POINT = 14; + public final static int T_DOT = 15; + private final AIFile mAI; private final ArrayList words = new ArrayList(); @@ -44,13 +59,15 @@ public class WordParser { private int line_counter = 0; private int char_counter = 0; private final String code; + private int version; - public WordParser(AIFile ai) { + public WordParser(AIFile ai, int version) { mAI = ai; this.code = ai.getCode(); + this.version = version; } - public void compile() throws LeekCompilerException { + public void compile(WordCompiler compiler) throws LeekCompilerException { words.clear(); line_counter = 1; char_counter = 0; @@ -65,38 +82,42 @@ public void compile() throws LeekCompilerException { int length = code.length(); for(int i = 0; i < code.length(); i++){ c = code.charAt(i); - if(c == '\r') continue; + if (c == '\r') continue; // Compteur caractères/lignes - if(c == '\n'){ + if (c == '\n') { + if (type != T_NOTHING) { + newWord(word, type); + type = T_NOTHING; + word = ""; + } char_counter = 0; line_counter++; comment_line = false; } else char_counter++; - if((c == '"' || c == '\'') && !comment_block && !comment_line){ - if(type == T_NOTHING){ + if ((c == '"' || c == '\'') && !comment_block && !comment_line) { + if (type == T_NOTHING) { word = ""; type = T_VAR_STRING; opener = c; } - else if(type == T_VAR_STRING && opener == c){ + else if (type == T_VAR_STRING && opener == c) { boolean isEscaped = false; - for(int j = word.length() - 1; j >= 0; j--){ + for (int j = word.length() - 1; j >= 0; j--) { if(word.charAt(j) == '\\') isEscaped = !isEscaped; else break; } - if(isEscaped){ + if (isEscaped) { word = word.substring(0, word.length() - 1); word += c; - } - else{ + } else { newWord(word, type); word = ""; type = T_NOTHING; } } - else if(type == T_VAR_STRING) word += c; - else{ + else if (type == T_VAR_STRING) word += c; + else { newWord(word, type); word = ""; opener = c; @@ -104,40 +125,40 @@ else if(type == T_VAR_STRING && opener == c){ } continue; } - if(type == T_VAR_STRING){ + if (type == T_VAR_STRING) { word += c; continue; } - if(c == '/' && i > 1 && code.charAt(i - 1) == '*' && comment_block && code.charAt(i - 2) != '/'){ + if (comment_block && c == '*' && length > i + 1 && code.charAt(i + 1) == '/') { comment_block = false; + i++; continue; } - if(comment_line || comment_block) continue; - if(c == '/' && length > i + 1 && code.charAt(i + 1) == '/'){ + if (comment_line || comment_block) continue; + if (c == '/' && length > i + 1 && code.charAt(i + 1) == '/') { comment_line = true; + if (version >= 2) i++; continue; } - else if(c == '/' && length > i + 1 && code.charAt(i + 1) == '*'){ + else if (c == '/' && length > i + 1 && code.charAt(i + 1) == '*') { comment_block = true; + if (version >= 2) i++; continue; } - if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_'){ - if(type == T_NOTHING){ + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_') { + if (type == T_NOTHING) { word += c; type = T_STRING; - } - else if(type == T_STRING || type == T_VAR_STRING){ + } else if (type == T_STRING || type == T_VAR_STRING) { word += c; - } - else{ - if(type == T_OPERATOR){ + } else { + if (type == T_OPERATOR) { newWord(word, type); word = "" + c; type = T_STRING; - } - else if(type == T_NUMBER){ - throw new LeekCompilerException(mAI, line_counter, char_counter, word, LeekCompilerException.INVALID_NUMBER); + } else if (type == T_NUMBER) { + throw new LeekCompilerException(mAI, line_counter, char_counter, word, Error.INVALID_NUMBER); } } } @@ -161,15 +182,29 @@ else if(c == ':'){ word = ""; type = T_NOTHING; } - else if(c == '.'){ - if(type == T_NUMBER || type == T_VAR_STRING){ + else if(c == '.') { + if (type == T_VAR_STRING) { word += c; + } else if (type == T_NUMBER) { + if (word.contains(".")) { + compiler.addError(new AnalyzeError(new IAWord(mAI, 0, ".", line_counter, char_counter + 1), AnalyzeErrorLevel.ERROR, Error.INVALID_CHAR)); + } else { + word += c; + } } - else{ - throw new LeekCompilerException(mAI, line_counter, char_counter, word, LeekCompilerException.INVALID_CHAR); + else if (version >= 2) { + if (type == T_STRING) { + newWord(word, type); + } + newWord(".", T_DOT); + word = ""; + type = T_NOTHING; + } else { + compiler.addError(new AnalyzeError(new IAWord(mAI, 0, ".", line_counter, char_counter + 1), AnalyzeErrorLevel.ERROR, Error.INVALID_CHAR)); + // throw new LeekCompilerException(mAI, line_counter, char_counter + 1, ".", Error.INVALID_CHAR); } } - else if(c == '@' || c == '+' || c == '=' || c == '<' || c == '>' || c == '|' || c == '&' || c == '-' || c == '/' || c == '*' || c == '%' || c == '!' || c == '?' || c == '^'){ + else if(c == '@' || c == '+' || c == '=' || c == '<' || c == '>' || c == '|' || c == '&' || c == '-' || c == '/' || c == '*' || c == '%' || c == '!' || c == '?' || c == '^' || c == '~' || c == '.'){ if(type == T_VAR_STRING){ word += c; } @@ -244,7 +279,7 @@ else if(word.equals("<<")){ word = "" + c; } } - else if(word.equals("*") || word.equals("/") || word.equals("%") || word.equals("=") || word.equals("!") || word.equals("<") || word.equals(">") || word.equals("^") || word.equals("==") || word.equals("!=")){ + else if(word.equals("*") || word.equals("**") || word.equals("/") || word.equals("%") || word.equals("=") || word.equals("!") || word.equals("<") || word.equals(">") || word.equals("^") || word.equals("==") || word.equals("!=")){ if(c == '=') word += c; else{ newWord(word, type); @@ -290,7 +325,7 @@ else if(c == '{' || c == '}'){ if(c == '{') newWord("{", T_ACCOLADE_LEFT); else newWord("}", T_ACCOLADE_RIGHT); } - else if(c == ' ' || c == '\n' || c == '\t'){ + else if(c == ' ' || c == '\n' || c == '\t' || c == 160 /* NBSP */){ if(type == T_VAR_STRING){ word += c; } @@ -315,14 +350,12 @@ else if(c == ','){ type = T_NOTHING; } newWord(",", T_VIRG); - } - else{ + } else { // Tout autre caractère - if(type == T_VAR_STRING){ + if (type == T_VAR_STRING) { word += c; - } - else{ - throw new LeekCompilerException(mAI, line_counter, char_counter, "" + c, LeekCompilerException.INVALID_CHAR); + } else { + throw new LeekCompilerException(mAI, line_counter, char_counter, "" + c, Error.INVALID_CHAR); } } } @@ -330,24 +363,33 @@ else if(c == ','){ if(type != T_NOTHING){ newWord(word, type); } - /* - * for(int i=0;i "+words.get(i).getWord( - * )); - * } - */ + + // for(int i=0;i "+words.get(i).getWord()); + // } + } + + private boolean wordEquals(String word, String expected) { + if (version <= 2) { + return word.equalsIgnoreCase(expected); + } + return word.equals(expected); } private void newWord(String word, int type) { - if(type == T_STRING){ - if(word.equalsIgnoreCase("or")){ + if (type == T_STRING) { + if (wordEquals(word, "or")) { type = T_OPERATOR; word = "||"; } - else if(word.equalsIgnoreCase("and")){ + else if (wordEquals(word, "and")) { type = T_OPERATOR; word = "&&"; } + else if (wordEquals(word, "instanceof")) { + type = T_OPERATOR; + word = "instanceof"; + } /* * else if(word.equalsIgnoreCase("not")){ * type = T_OPERATOR; @@ -355,8 +397,8 @@ else if(word.equalsIgnoreCase("and")){ * } */ } - else if(type == T_OPERATOR){ - if(word.equals("=!")){ + else if (type == T_OPERATOR) { + if (word.equals("=!")) { words.add(new IAWord(mAI, type, "=", line_counter, char_counter)); words.add(new IAWord(mAI, type, "!", line_counter, char_counter)); return; @@ -386,6 +428,10 @@ public void skipWord() { cursor++; } + public void back() { + cursor--; + } + public boolean haveWords() { return cursor < words.size(); } @@ -402,4 +448,11 @@ public int getInstructionCount() { return instructions; } + public int getPosition() { + return cursor; + } + + public void setPosition(int position) { + this.cursor = position; + } } diff --git a/src/main/java/leekscript/compiler/bloc/AbstractLeekBlock.java b/src/main/java/leekscript/compiler/bloc/AbstractLeekBlock.java index 2480d031..ae4671d3 100644 --- a/src/main/java/leekscript/compiler/bloc/AbstractLeekBlock.java +++ b/src/main/java/leekscript/compiler/bloc/AbstractLeekBlock.java @@ -1,25 +1,31 @@ package leekscript.compiler.bloc; import java.util.ArrayList; +import java.util.HashMap; import leekscript.compiler.AIFile; +import leekscript.compiler.IAWord; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.exceptions.LeekCompilerException; -import leekscript.compiler.exceptions.LeekInstructionException; +import leekscript.compiler.expression.LeekVariable; import leekscript.compiler.instruction.LeekInstruction; +import leekscript.common.Error; public abstract class AbstractLeekBlock implements LeekInstruction { + protected ArrayList mInstructions = new ArrayList(); protected AbstractLeekBlock mParent = null; - protected ArrayList mVariables = new ArrayList(); + protected HashMap mVariables = new HashMap<>(); - protected String mDeclaringVariable = null; + protected IAWord mDeclaringVariable = null; protected boolean mDeclaringVariableUsed = false; protected boolean mAccolade = true; protected MainLeekBlock mMain = null; protected int mEndInstruction = 0; protected int mLine = 0; protected AIFile mAI = null; + protected boolean full = false; public AbstractLeekBlock getParent() { return mParent; @@ -55,9 +61,9 @@ public AbstractLeekBlock(AbstractLeekBlock parent, MainLeekBlock main, int line, mAI = ai; } - public void addInstruction(LeekInstruction instruction) throws LeekInstructionException { + public void addInstruction(WordCompiler compiler, LeekInstruction instruction) throws LeekCompilerException { if (mEndInstruction != 0) { - throw new LeekInstructionException(LeekCompilerException.CANT_ADD_INSTRUCTION_AFTER_BREAK); + throw new LeekCompilerException(compiler.getParser().lastWord(), Error.CANT_ADD_INSTRUCTION_AFTER_BREAK); } mEndInstruction = instruction.getEndBlock(); mInstructions.add(instruction); @@ -71,17 +77,17 @@ public boolean hasAccolade() { return mAccolade; } - public void setDeclaringVariable(String variable) { + public void setDeclaringVariable(IAWord variable) { mDeclaringVariable = variable; mDeclaringVariableUsed = false; } - public String getDeclaringVariable() { + public IAWord getDeclaringVariable() { mDeclaringVariableUsed = true; return mDeclaringVariable; } - public boolean isDeclaringBariableUsed() { + public boolean isDeclaringVariableUsed() { return mDeclaringVariableUsed; } @@ -91,7 +97,7 @@ public void checkEndBlock() { } public AbstractLeekBlock endInstruction() { - if (!mAccolade && mParent != null && mInstructions.size() == 1) { + if (!mAccolade && mParent != null && isFull()) { // if(this instanceof ConditionalBloc){ // ConditionalBloc bloc = (ConditionalBloc) this; // do{ @@ -104,8 +110,8 @@ public AbstractLeekBlock endInstruction() { return this; } - public void addVariable(String variable) { - mVariables.add(variable); + public void addVariable(LeekVariable variable) { + mVariables.put(variable.getName(), variable); } public LeekInstruction lastInstruction() { @@ -113,11 +119,15 @@ public LeekInstruction lastInstruction() { } public boolean hasVariable(String variable) { - if (mVariables.contains(variable)) - return true; + return getVariable(variable, false) != null; + } + + public LeekVariable getVariable(String variable, boolean includeClassMembers) { + var v = mVariables.get(variable); + if (v != null) return v; if (mParent != null) - return mParent.hasVariable(variable); - return false; + return mParent.getVariable(variable, includeClassMembers); + return null; } public boolean hasGlobal(String globale) { @@ -148,6 +158,11 @@ public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { } } + @Override + public int getOperations() { + return 0; + } + public int countInstructions() { return mInstructions.size() == 0 ? 1 : mInstructions.size(); } @@ -167,4 +182,28 @@ public int getEndBlock() { public boolean putCounterBefore() { return mEndInstruction != 0; } + + public void analyze(WordCompiler compiler) { + AbstractLeekBlock initialBlock = compiler.getCurrentBlock(); + compiler.setCurrentBlock(this); + for (var instruction : mInstructions) { + instruction.analyze(compiler); + } + compiler.setCurrentBlock(initialBlock); + } + + public int getLine() { + return mLine; + } + public AIFile getFile() { + return mAI; + } + + public void setFull(boolean full) { + this.full = full; + } + + public boolean isFull() { + return lastInstruction() != null || this.full; + } } diff --git a/src/main/java/leekscript/compiler/bloc/AnonymousFunctionBlock.java b/src/main/java/leekscript/compiler/bloc/AnonymousFunctionBlock.java index 0166ec91..a73352a5 100644 --- a/src/main/java/leekscript/compiler/bloc/AnonymousFunctionBlock.java +++ b/src/main/java/leekscript/compiler/bloc/AnonymousFunctionBlock.java @@ -3,11 +3,17 @@ import java.util.ArrayList; import leekscript.compiler.AIFile; +import leekscript.compiler.IAWord; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; +import leekscript.compiler.expression.LeekVariable; +import leekscript.compiler.expression.LeekVariable.VariableType; +import leekscript.compiler.instruction.LeekVariableDeclarationInstruction; public class AnonymousFunctionBlock extends AbstractLeekBlock { private final ArrayList mParameters = new ArrayList(); + private final ArrayList mParameterDeclarations = new ArrayList<>(); private final ArrayList mReferences = new ArrayList(); private int mId = 0; @@ -37,21 +43,38 @@ public String referenceArray() { return str + "}"; } - public void addParameter(String parameter, boolean is_reference) { - mParameters.add(parameter); + public void addParameter(WordCompiler compiler, IAWord token, boolean is_reference) { + mParameters.add(token.getWord()); mReferences.add(is_reference); - addVariable(parameter); + var declaration = new LeekVariableDeclarationInstruction(compiler, token, token.getLine(), token.getAI(), this); + mParameterDeclarations.add(declaration); + addVariable(new LeekVariable(token, VariableType.ARGUMENT, declaration)); + } + + public boolean hasParameter(String name) { + for (var parameter : mParameters) { + if (parameter.equals(name)) return true; + } + return false; } @Override public String getCode() { - String str = "function anonymous" + mId + "("; + String str = "function("; for (int i = 0; i < mParameters.size(); i++) { if (i != 0) str += ", "; str += mParameters.get(i); } - return str + "){\n" + super.getCode() + "}\n"; + return str + ") {\n" + super.getCode() + "}\n"; + } + + @Override + public void analyze(WordCompiler compiler) { + var initialFunction = compiler.getCurrentFunction(); + compiler.setCurrentFunction(this); + super.analyze(compiler); + compiler.setCurrentFunction(initialFunction); } @Override @@ -61,26 +84,53 @@ public void checkEndBlock() { @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { + var previousFunction = mainblock.getWordCompiler().getCurrentFunction(); + mainblock.getWordCompiler().setCurrentFunction(this); StringBuilder sb = new StringBuilder(); sb.append("new LeekAnonymousFunction() {"); - sb.append("public AbstractLeekValue run(AI mUAI, AbstractLeekValue[] values) throws Exception {"); + sb.append("public Object run(ObjectLeekValue thiz, Object... values) throws LeekRunException {"); for (int i = 0; i < mParameters.size(); i++) { - sb.append("final VariableLeekValue user_").append(mParameters.get(i)).append(" = "); - if (mReferences.get(i)) { - sb.append("(values[").append(i).append("] instanceof VariableLeekValue)?(VariableLeekValue)values[").append(i).append("]:"); + var parameter = mParameters.get(i); + var declaration = mParameterDeclarations.get(i); + if (declaration.isCaptured()) { + sb.append("final var u_").append(parameter).append(" = new Wrapper("); + if (mReferences.get(i)) { + sb.append("(values[").append(i).append("] instanceof Box) ? (Box) values[").append(i).append("] : "); + } + sb.append("new Box(" + writer.getAIThis() + ", "); + sb.append("values[").append(i).append("]));"); + } else { + sb.append("var u_").append(parameter).append(" = "); + + if (mainblock.getWordCompiler().getVersion() >= 2) { + sb.append("values[").append(i).append("]; ops(1);"); + } else { + // In LeekScript 1.0, load the value or reference + if (mReferences.get(i)) { + sb.append("values[").append(i).append("] instanceof Box ? (Box) values[").append(i).append("] : new Box(" + writer.getAIThis() + ", load(values[").append(i).append("]));"); + } else { + // sb.append("new Box(" + writer.getAIThis() + ", values[").append(i).append("] instanceof Box ? copy(load(values[").append(i).append("])) : copy(load(values[").append(i).append("])));"); + sb.append("new Box(" + writer.getAIThis() + ", values[").append(i).append("]);"); + } + } } - sb.append("new VariableLeekValue(mUAI, values[").append(i).append("].getValue());"); } writer.addLine(sb.toString(), mLine, mAI); writer.addCounter(1); super.writeJavaCode(mainblock, writer); if (mEndInstruction == 0) - writer.addLine("return LeekValueManager.NULL;"); + writer.addLine("return null;"); writer.addCode("}}"); + mainblock.getWordCompiler().setCurrentFunction(previousFunction); } public boolean isReference(int i) { return mReferences.get(i); } + + @Override + public int getOperations() { + return 0; + } } diff --git a/src/main/java/leekscript/compiler/bloc/ClassMethodBlock.java b/src/main/java/leekscript/compiler/bloc/ClassMethodBlock.java new file mode 100644 index 00000000..734b848f --- /dev/null +++ b/src/main/java/leekscript/compiler/bloc/ClassMethodBlock.java @@ -0,0 +1,122 @@ +package leekscript.compiler.bloc; + +import java.util.ArrayList; +import java.util.List; + +import leekscript.compiler.AIFile; +import leekscript.compiler.IAWord; +import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; +import leekscript.compiler.expression.LeekVariable; +import leekscript.compiler.expression.LeekVariable.VariableType; +import leekscript.compiler.instruction.ClassDeclarationInstruction; +import leekscript.compiler.instruction.LeekVariableDeclarationInstruction; + +public class ClassMethodBlock extends AbstractLeekBlock { + + private final ClassDeclarationInstruction clazz; + private final boolean isStatic; + private final ArrayList mParameters = new ArrayList<>(); + private final ArrayList mParameterDeclarations = new ArrayList<>(); + private int mId = 0; + + public ClassMethodBlock(ClassDeclarationInstruction clazz, boolean isStatic, AbstractLeekBlock parent, MainLeekBlock main, int line, AIFile ai) { + super(parent, main, line, ai); + this.clazz = clazz; + this.isStatic = isStatic; + } + + public void setId(int id) { + mId = id; + } + + public int getId() { + return mId; + } + + public int countParameters() { + return mParameters.size(); + } + + public String referenceArray() { + String str = "{"; + for (int i = 0; i < mParameters.size(); i++) { + if (i != 0) + str += ","; + } + return str + "}"; + } + + public void addParameter(WordCompiler compiler, IAWord token) { + mParameters.add(token); + var declaration = new LeekVariableDeclarationInstruction(compiler, token, token.getLine(), token.getAI(), this); + mParameterDeclarations.add(declaration); + addVariable(new LeekVariable(token, VariableType.ARGUMENT, declaration)); + } + + public LeekVariable getVariable(String variable, boolean includeClassMembers) { + // Arguments + // for (var parameter : mParameters) { + // if (parameter.getWord().equals(variable)) { + // return new LeekVariable(parameter, VariableType.ARGUMENT); + // } + // } + + var v = super.getVariable(variable, includeClassMembers); + if (v != null) return v; + + // Search in fields + if (variable.equals("class")) return new LeekVariable(new IAWord("class"), VariableType.THIS_CLASS); + if (!isStatic) { + if (variable.equals("this")) return new LeekVariable(new IAWord("this"), VariableType.THIS); + if (includeClassMembers) { + v = clazz.getMember(variable); + if (v != null) return v; + } + } + if (includeClassMembers) { + v = clazz.getStaticMember(variable); + if (v != null) return v; + } + return null; + } + + @Override + public String getCode() { + String str = "("; + for (int i = 0; i < mParameters.size(); i++) { + if (i != 0) + str += ", "; + str += mParameters.get(i); + } + return str + ") {\n" + super.getCode() + "}\n"; + } + + @Override + public void checkEndBlock() { + + } + + @Override + public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { + + writer.addLine("", mLine, mAI); + + super.writeJavaCode(mainblock, writer); + if (mEndInstruction == 0) { + writer.addLine("return null;"); + } + } + + public ClassDeclarationInstruction getClassDeclaration() { + return this.clazz; + } + + public List getParameters() { + return mParameters; + } + + public ArrayList getParametersDeclarations() { + return mParameterDeclarations; + } +} diff --git a/src/main/java/leekscript/compiler/bloc/ConditionalBloc.java b/src/main/java/leekscript/compiler/bloc/ConditionalBloc.java index 67a49783..642102c5 100644 --- a/src/main/java/leekscript/compiler/bloc/ConditionalBloc.java +++ b/src/main/java/leekscript/compiler/bloc/ConditionalBloc.java @@ -2,10 +2,10 @@ import leekscript.compiler.AIFile; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.expression.AbstractExpression; -import leekscript.compiler.instruction.LeekInstruction; -public class ConditionalBloc extends AbstractLeekBlock implements LeekInstruction { +public class ConditionalBloc extends AbstractLeekBlock { private ConditionalBloc mParentCondition = null; private AbstractExpression mCondition = null; @@ -35,36 +35,39 @@ public AbstractExpression getCondition() { @Override public String getCode() { String str = ""; - if(mParentCondition == null) str = "if(" + mCondition.getString() + "){"; - else if(mCondition != null) str = "elseif(" + mCondition.getString() + "){"; - else str = "else{"; + if(mParentCondition == null) str = "if (" + mCondition.getString() + ") {"; + else if(mCondition != null) str = "else if (" + mCondition.getString() + ") {"; + else str = "else {"; str += "\n" + super.getCode(); return str + "}"; } @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - if(mParentCondition == null){ - writer.addCode("if("); - mCondition.writeJavaCode(mainblock, writer); - writer.addLine(".getBoolean()){", mLine, mAI); + if (mParentCondition == null) { + writer.addCode("if ("); + writer.addCode("ops("); + writer.getBoolean(mainblock, mCondition); + writer.addCode(", " + mCondition.getOperations()); + writer.addLine(")) {", mLine, mAI); + } else if (mCondition != null) { + writer.addCode("else if ("); + writer.addCode("ops("); + writer.getBoolean(mainblock, mCondition); + writer.addCode(", " + mCondition.getOperations()); + writer.addLine(")) {", mLine, mAI); } - else if(mCondition != null){ - writer.addCode("else if("); - mCondition.writeJavaCode(mainblock, writer); - writer.addLine(".getBoolean()){", mLine, mAI); - } - else writer.addLine("else{", mLine, mAI); + else writer.addLine("else {", mLine, mAI); super.writeJavaCode(mainblock, writer); - if(mEndInstruction == 0) writer.addCounter(1); + if (mEndInstruction == 0) writer.addCounter(1); writer.addLine("}"); } public int getConditionEndBlock() { - if(mEndInstruction == 0) return 0; - if(mParentCondition != null){ + if (mEndInstruction == 0) return 0; + if (mParentCondition != null) { int parent = mParentCondition.getConditionEndBlock(); - if(parent == 0) return 0; + if (parent == 0) return 0; return parent | mEndInstruction; } return mEndInstruction; @@ -72,9 +75,9 @@ public int getConditionEndBlock() { @Override public int getEndBlock() { - if(mCondition == null){ + if (mCondition == null) { int r = getConditionEndBlock(); - if(r != 0) setPutCounterBefore(true); + if (r != 0) setPutCounterBefore(true); return r; } return 0; @@ -86,8 +89,15 @@ public boolean putCounterBefore() { } private void setPutCounterBefore(boolean value) { - if(mParentCondition != null) mParentCondition.setPutCounterBefore(value); + if (mParentCondition != null) mParentCondition.setPutCounterBefore(value); else mPutCounterBefore = value; } + @Override + public void analyze(WordCompiler compiler) { + if (mCondition != null) { + mCondition.analyze(compiler); + } + super.analyze(compiler); + } } diff --git a/src/main/java/leekscript/compiler/bloc/DoWhileBlock.java b/src/main/java/leekscript/compiler/bloc/DoWhileBlock.java index a21c334d..938fea0a 100644 --- a/src/main/java/leekscript/compiler/bloc/DoWhileBlock.java +++ b/src/main/java/leekscript/compiler/bloc/DoWhileBlock.java @@ -2,7 +2,9 @@ import leekscript.compiler.AIFile; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.expression.AbstractExpression; +import leekscript.compiler.expression.LeekBoolean; public class DoWhileBlock extends AbstractLeekBlock { @@ -27,16 +29,31 @@ public String getCode() { @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - writer.addLine("do{"); + writer.addLine("do {"); writer.addCounter(1); super.writeJavaCode(mainblock, writer); - writer.addCode("}while("); - mCondition.writeJavaCode(mainblock, writer); - writer.addLine(".getBoolean());", mLine, mAI); + writer.addCode("} while (ops("); + // Prevent unreachable code error + if (mCondition instanceof LeekBoolean) { + writer.addCode("bool("); + writer.getBoolean(mainblock, mCondition); + writer.addCode(")"); + } else { + writer.getBoolean(mainblock, mCondition); + } + writer.addCode(", " + mCondition.getOperations() + ")"); + writer.addLine(");", mLine, mAI); } @Override public boolean isBreakable() { return true; } + + public void analyze(WordCompiler compiler) { + if (mCondition != null) { + mCondition.analyze(compiler); + } + super.analyze(compiler); + } } diff --git a/src/main/java/leekscript/compiler/bloc/ForBlock.java b/src/main/java/leekscript/compiler/bloc/ForBlock.java index 1ca3440f..62852e41 100644 --- a/src/main/java/leekscript/compiler/bloc/ForBlock.java +++ b/src/main/java/leekscript/compiler/bloc/ForBlock.java @@ -1,12 +1,15 @@ package leekscript.compiler.bloc; import leekscript.compiler.AIFile; +import leekscript.compiler.IAWord; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.expression.AbstractExpression; +import leekscript.compiler.expression.LeekBoolean; import leekscript.compiler.expression.LeekExpression; -import leekscript.compiler.expression.LeekGlobal; import leekscript.compiler.expression.LeekVariable; import leekscript.compiler.expression.Operators; +import leekscript.compiler.expression.LeekVariable.VariableType; import leekscript.compiler.instruction.LeekExpressionInstruction; import leekscript.compiler.instruction.LeekInstruction; import leekscript.compiler.instruction.LeekVariableDeclarationInstruction; @@ -21,16 +24,15 @@ public ForBlock(AbstractLeekBlock parent, MainLeekBlock main, int line, AIFile ai) { + public ForeachBlock(AbstractLeekBlock parent, MainLeekBlock main, boolean isDeclaration, int line, AIFile ai, boolean reference) { super(parent, main, line, ai); mIsDeclaration = isDeclaration; + mReference = reference; } - public void setIterator(String iterator, boolean declaration) { - if(declaration) addVariable(iterator); + public void setIterator(WordCompiler compiler, IAWord iterator) { mIterator = iterator; + if (mIsDeclaration) { + declaration = new LeekVariableDeclarationInstruction(compiler, iterator, iterator.getLine(), iterator.getAI(), compiler.getCurrentFunction()); + } } public void setArray(AbstractExpression exp) { @@ -26,24 +39,61 @@ public void setArray(AbstractExpression exp) { @Override public String getCode() { - return "for(" + mIterator + " in " + mArray.getString() + "){\n" + super.getCode() + "}"; + return "for (" + (mIsDeclaration ? "var " : "") + mIterator + " in " + mArray.getString() + ") {\n" + super.getCode() + "}"; } @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - //On prend un nombre unique pour les noms de variables temporaires + // On prend un nombre unique pour les noms de variables temporaires int block_count = getCount(); String var = "i" + block_count; String ar = "ar" + block_count; - String iterator_name = mainblock.hasGlobal(mIterator) ? ("globale_" + mIterator) : "user_" + mIterator; - - writer.addCode("final AbstractLeekValue " + ar + " = "); - mArray.writeJavaCode(mainblock, writer); - writer.addLine(".getValue();", mLine, mAI); - writer.addLine("if(" + ar + ".isArray()){"); - if(mIsDeclaration) writer.addLine("final VariableLeekValue " + iterator_name + " = new VariableLeekValue(mUAI, LeekValueManager.NULL);"); - else writer.addLine(iterator_name + ".set(mUAI, LeekValueManager.NULL);"); - writer.addLine("for(AbstractLeekValue " + var + " : " + ar + ".getArray()){ " + iterator_name + ".set(mUAI, " + var + ".getValue());"); + String iterator_name = mainblock.hasGlobal(mIterator.getWord()) ? ("g_" + mIterator) : "u_" + mIterator; + + // Container + writer.addCode("final var " + ar + " = ops("); + if (mainblock.getCompiler().getCurrentAI().getVersion() >= 2) { + mArray.writeJavaCode(mainblock, writer); + } else { + writer.compileLoad(mainblock, mArray); + } + writer.addCode(", " + mArray.getOperations() + ");"); + + writer.addLine("if (isIterable(" + ar + ")) {", mIterator.getLine(), mIterator.getAI()); + if (mIsDeclaration) { + if (mIsDeclaration && declaration.isCaptured()) { + writer.addCode("final Wrapper " + iterator_name + " = new Wrapper(new Box(" + writer.getAIThis() + ", null));"); + } else if (mainblock.getCompiler().getCurrentAI().getVersion() >= 2) { + writer.addLine("Object " + iterator_name + " = null;"); + writer.addCounter(1); + } else { + writer.addLine("var " + iterator_name + " = new Box(" + writer.getAIThis() + ", null);"); + } + } else { + writer.addCounter(1); + } + writer.addLine("for (var " + var + " : (ArrayLeekValue) " + ar + ") {"); + + if (mainblock.getCompiler().getCurrentAI().getVersion() >= 2) { + if (mIsDeclaration && declaration.isCaptured()) { + writer.addLine(iterator_name + ".set(" + var + ".getValue());"); + } else if (mReference) { + writer.addLine(iterator_name + " = " + var + ".getValue();"); + writer.addCounter(1); + } else { + writer.addLine(iterator_name + " = " + var + ".getValue();"); + } + } else { + if (mReference) { + writer.addCode(iterator_name + ".set(" + var + ".getValue());"); + } else if (mIsDeclaration && declaration.isCaptured()) { + writer.addLine(iterator_name + ".set(" + var + ".getValue());"); + writer.addCounter(1); + } else { + writer.addLine(iterator_name + ".set(" + var + ".getValue());"); + writer.addCounter(1); + } + } writer.addCounter(1); super.writeJavaCode(mainblock, writer); @@ -59,4 +109,27 @@ public boolean isBreakable() { public int getEndBlock() { return 0; } + + public void analyze(WordCompiler compiler) { + AbstractLeekBlock initialBlock = compiler.getCurrentBlock(); + compiler.setCurrentBlock(this); + // Si c'est une déclaration on vérifie que le nom est disponnible + if (mIsDeclaration) { + if ((compiler.getVersion() >= 2 && (compiler.getMainBlock().hasGlobal(mIterator.getWord()) || compiler.getMainBlock().hasUserFunction(mIterator.getWord(), true))) || compiler.getCurrentBlock().hasVariable(mIterator.getWord())) { + compiler.addError(new AnalyzeError(mIterator, AnalyzeErrorLevel.ERROR, Error.VARIABLE_NAME_UNAVAILABLE)); + } else { + this.addVariable(new LeekVariable(mIterator, VariableType.LOCAL, declaration)); + } + declaration.setFunction(compiler.getCurrentFunction()); + } else { + var v = compiler.getCurrentBlock().getVariable(mIterator.getWord(), true); + if (v == null) { + compiler.addError(new AnalyzeError(mIterator, AnalyzeErrorLevel.ERROR, Error.UNKNOWN_VARIABLE_OR_FUNCTION)); + } + } + mArray.analyze(compiler); + compiler.setCurrentBlock(initialBlock); + + super.analyze(compiler); + } } diff --git a/src/main/java/leekscript/compiler/bloc/ForeachKeyBlock.java b/src/main/java/leekscript/compiler/bloc/ForeachKeyBlock.java index bb89d7d8..024b4dbc 100644 --- a/src/main/java/leekscript/compiler/bloc/ForeachKeyBlock.java +++ b/src/main/java/leekscript/compiler/bloc/ForeachKeyBlock.java @@ -1,31 +1,51 @@ package leekscript.compiler.bloc; import leekscript.compiler.AIFile; +import leekscript.compiler.AnalyzeError; +import leekscript.compiler.IAWord; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; +import leekscript.compiler.AnalyzeError.AnalyzeErrorLevel; import leekscript.compiler.expression.AbstractExpression; +import leekscript.compiler.expression.LeekVariable; +import leekscript.compiler.expression.LeekVariable.VariableType; +import leekscript.common.Error; +import leekscript.compiler.instruction.LeekVariableDeclarationInstruction; public class ForeachKeyBlock extends AbstractLeekBlock { - private String mIterator; + private IAWord mIterator; private AbstractExpression mArray; private boolean mIsDeclaration = false; - private String mKeyIterator = null; + private IAWord mKeyIterator = null; private boolean mIsKeyDeclaration = false; + private boolean mKeyReference = false; + private boolean mValueReference = false; + private LeekVariableDeclarationInstruction iteratorDeclaration; + private LeekVariableDeclarationInstruction iteratorKeyDeclaration; - public ForeachKeyBlock(AbstractLeekBlock parent, MainLeekBlock main, boolean isKeyDeclaration, boolean isValueDeclaration, int line, AIFile ai) { + public ForeachKeyBlock(AbstractLeekBlock parent, MainLeekBlock main, boolean isKeyDeclaration, boolean isValueDeclaration, int line, AIFile ai, boolean keyReference, boolean valueReference) { super(parent, main, line, ai); mIsDeclaration = isValueDeclaration; mIsKeyDeclaration = isKeyDeclaration; + mKeyReference = keyReference; + mValueReference = valueReference; } - public void setValueIterator(String iterator, boolean declaration) { - if(declaration) addVariable(iterator); + public void setValueIterator(WordCompiler compiler, IAWord iterator, boolean declaration) { + if (declaration) { + iteratorDeclaration = new LeekVariableDeclarationInstruction(compiler, iterator, 0, compiler.getAI(), compiler.getCurrentFunction()); + // addVariable(new LeekVariable(iterator, VariableType.ITERATOR, iteratorDeclaration)); + } mIterator = iterator; } - public void setKeyIterator(String iterator, boolean declaration) { - if(declaration) addVariable(iterator); + public void setKeyIterator(WordCompiler compiler, IAWord iterator, boolean declaration) { + if (declaration) { + iteratorKeyDeclaration = new LeekVariableDeclarationInstruction(compiler, iterator, 0, compiler.getAI(), compiler.getCurrentFunction()); + // addVariable(new LeekVariable(iterator, VariableType.ITERATOR, iteratorKeyDeclaration)); + } mKeyIterator = iterator; } @@ -35,44 +55,101 @@ public void setArray(AbstractExpression exp) { @Override public String getCode() { - return "for(" + mIterator + " in " + mArray.getString() + "){\n" + super.getCode() + "}"; + return "for (" + (mIsKeyDeclaration ? "var " : "") + mKeyIterator + " : " + (mIsDeclaration ? "var " : "") + mIterator + " in " + mArray.getString() + ") {\n" + super.getCode() + "}"; } @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - //On prend un nombre unique pour les noms de variables temporaires + // On prend un nombre unique pour les noms de variables temporaires int block_count = getCount(); String var = "i" + block_count; String ar = "ar" + block_count; - String key_iterator = mainblock.hasGlobal(mKeyIterator) ? ("globale_" + mKeyIterator) : ("user_" + mKeyIterator); - String val_iterator = mainblock.hasGlobal(mIterator) ? ("globale_" + mIterator) : ("user_" + mIterator); - writer.addCode("final AbstractLeekValue " + ar + " = "); - mArray.writeJavaCode(mainblock, writer); - writer.addCode(".getValue();"); + String key_iterator = mainblock.hasGlobal(mKeyIterator.getWord()) ? ("g_" + mKeyIterator) : ("u_" + mKeyIterator); + String val_iterator = mainblock.hasGlobal(mIterator.getWord()) ? ("g_" + mIterator) : ("u_" + mIterator); + + // Container + writer.addCode("final var " + ar + " = ops("); + if (mainblock.getCompiler().getCurrentAI().getVersion() >= 2) { + mArray.writeJavaCode(mainblock, writer); + } else { + writer.compileLoad(mainblock, mArray); + } + writer.addCode(", " + mArray.getOperations() + ");"); + StringBuilder sb = new StringBuilder(); - sb.append("if(").append(ar).append(".isArray()){"); - //Clé - if(mIsKeyDeclaration) sb.append("final VariableLeekValue ").append(key_iterator).append(" = new VariableLeekValue(mUAI, LeekValueManager.NULL);"); - else sb.append(key_iterator).append(".set(mUAI, LeekValueManager.NULL);"); - //Valeur - if(mIsDeclaration) sb.append("final VariableLeekValue ").append(val_iterator).append(" = new VariableLeekValue(mUAI, LeekValueManager.NULL);"); - else sb.append(val_iterator).append(".set(mUAI, LeekValueManager.NULL);"); - //On fait le parcour - //Déclaration de la variable - sb.append("ArrayLeekValue.ArrayIterator ").append(var).append("=").append(ar).append(".getArray().getArrayIterator();"); - sb.append("while(!").append(var).append(".ended()){"); - //Maj des variables - sb.append(key_iterator).append(".set(mUAI, ").append(var).append(".getKey(mUAI));"); - sb.append(val_iterator).append(".set(mUAI, ").append(var).append(".getValue(mUAI));"); + sb.append("if (isIterable(").append(ar).append(")) {"); + // Clé + if (mIsKeyDeclaration) { + if (iteratorKeyDeclaration.isCaptured()) { + sb.append("final Wrapper " + key_iterator + " = new Wrapper(new Box(" + writer.getAIThis() + ", null));"); + } else if (mainblock.getCompiler().getCurrentAI().getVersion() <= 1) { + sb.append("var " + key_iterator + " = new Box(" + writer.getAIThis() + ", null);"); + } else { + sb.append("Object ").append(key_iterator).append(" = null; ops(1); "); + } + } else { + sb.append("ops(1);"); + } + // Valeur + if (mIsDeclaration) { + if (iteratorDeclaration.isCaptured()) { + sb.append("final Wrapper " + val_iterator + " = new Wrapper(new Box(" + writer.getAIThis() + ", null));"); + } else if (mainblock.getCompiler().getCurrentAI().getVersion() >= 2) { + sb.append("Object " + val_iterator + " = null; ops(1);"); + } else if (mainblock.getCompiler().getCurrentAI().getVersion() <= 1 || (iteratorDeclaration != null && iteratorDeclaration.isCaptured())) { + sb.append("var " + val_iterator + " = new Box(" + writer.getAIThis() + ", null);"); + } else { + sb.append("Object ").append(val_iterator).append(" = null; ops(1);"); + } + } else { + sb.append("ops(1);"); + } + // On fait le parcours + // Déclaration de la variable + sb.append("var ").append(var).append(" = ((ArrayLeekValue) ").append(ar).append(").getArrayIterator(); "); + sb.append("while (!").append(var).append(".ended()) { ops(1); "); + // Maj de la clé + if (mainblock.getCompiler().getCurrentAI().getVersion() >= 2) { + if (mIsKeyDeclaration && iteratorKeyDeclaration.isCaptured()) { + sb.append(key_iterator).append(".set(").append(var).append(".getKeyRef());"); + } else { + sb.append(key_iterator).append(" = ").append(var).append(".getKeyRef();"); + } + } else { + if (mIsKeyDeclaration && iteratorKeyDeclaration.isCaptured()) { + sb.append(key_iterator).append(".set(").append(var).append(".getKeyRef()); ops(1); "); + } else if (mKeyReference) { + sb.append(key_iterator).append(".set(").append(var).append(".getKeyRef());"); + } else { + sb.append(key_iterator).append(".set(").append(var).append(".getKeyRef()); ops(1); "); + } + } + // Maj de la valeur + if (mainblock.getCompiler().getCurrentAI().getVersion() >= 2) { + if (mValueReference) { + sb.append(val_iterator).append(" = ").append(var).append(".value();"); + } else if (mIsDeclaration && iteratorDeclaration.isCaptured()) { + sb.append(val_iterator).append(".set(").append(var).append(".value());"); + } else { + sb.append(val_iterator).append(" = ").append(var).append(".value();"); + } + } else { + if (mValueReference) { + sb.append(val_iterator).append(".set(").append(var).append(".value());"); + } else if (mIsDeclaration && iteratorDeclaration.isCaptured()) { + sb.append(val_iterator).append(".set(").append(var).append(".value()); ops(1);"); + } else { + sb.append(val_iterator).append(".set(").append(var).append(".value()); ops(1);"); + } + } sb.append(var).append(".next();"); - writer.addCounter(1); writer.addLine(sb.toString(), mLine, mAI); - //Instructions + // Instructions super.writeJavaCode(mainblock, writer); - //Fin + // Fin writer.addLine("}}"); } @@ -85,4 +162,44 @@ public boolean isBreakable() { public int getEndBlock() { return 0; } + + public void analyze(WordCompiler compiler) { + AbstractLeekBlock initialBlock = compiler.getCurrentBlock(); + compiler.setCurrentBlock(this); + + // Si c'est une déclaration on vérifie que le nom est disponnible + if (mIsKeyDeclaration) { + if ((compiler.getVersion() >= 2 && (compiler.getMainBlock().hasGlobal(mKeyIterator.getWord()) || compiler.getMainBlock().hasUserFunction(mKeyIterator.getWord(), true))) || compiler.getCurrentBlock().hasVariable(mKeyIterator.getWord())) { + compiler.addError(new AnalyzeError(mKeyIterator, AnalyzeErrorLevel.ERROR, Error.VARIABLE_NAME_UNAVAILABLE)); + } else { + this.addVariable(new LeekVariable(mKeyIterator, VariableType.LOCAL, iteratorKeyDeclaration)); + } + } else { + var v = compiler.getCurrentBlock().getVariable(mKeyIterator.getWord(), true); + if (v == null) { + compiler.addError(new AnalyzeError(mKeyIterator, AnalyzeErrorLevel.ERROR, Error.UNKNOWN_VARIABLE_OR_FUNCTION)); + } + } + // Si c'est une déclaration on vérifie que le nom est disponnible + if (mIsDeclaration) { + if ((compiler.getVersion() >= 2 && (compiler.getMainBlock().hasGlobal(mIterator.getWord()) || compiler.getMainBlock().hasUserFunction(mIterator.getWord(), true))) || compiler.getCurrentBlock().hasVariable(mIterator.getWord())) { + compiler.addError(new AnalyzeError(mIterator, AnalyzeErrorLevel.ERROR, Error.VARIABLE_NAME_UNAVAILABLE)); + } else { + this.addVariable(new LeekVariable(mIterator, VariableType.LOCAL, iteratorDeclaration)); + } + } else { + var v = compiler.getCurrentBlock().getVariable(mIterator.getWord(), true); + if (v == null) { + compiler.addError(new AnalyzeError(mIterator, AnalyzeErrorLevel.ERROR, Error.UNKNOWN_VARIABLE_OR_FUNCTION)); + } + } + if (iteratorDeclaration != null) + iteratorDeclaration.setFunction(compiler.getCurrentFunction()); + if (iteratorKeyDeclaration != null) + iteratorKeyDeclaration.setFunction(compiler.getCurrentFunction()); + + mArray.analyze(compiler); + compiler.setCurrentBlock(initialBlock); + super.analyze(compiler); + } } diff --git a/src/main/java/leekscript/compiler/bloc/FunctionBlock.java b/src/main/java/leekscript/compiler/bloc/FunctionBlock.java index c0eab628..c4bfc269 100644 --- a/src/main/java/leekscript/compiler/bloc/FunctionBlock.java +++ b/src/main/java/leekscript/compiler/bloc/FunctionBlock.java @@ -3,17 +3,24 @@ import java.util.ArrayList; import leekscript.compiler.AIFile; +import leekscript.compiler.IAWord; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; +import leekscript.compiler.expression.LeekVariable; +import leekscript.compiler.expression.LeekVariable.VariableType; +import leekscript.compiler.instruction.LeekVariableDeclarationInstruction; public class FunctionBlock extends AbstractLeekBlock { - private String mName; + private IAWord token; private int mId; private final ArrayList mParameters = new ArrayList(); + private final ArrayList mParameterDeclarations = new ArrayList<>(); private final ArrayList mReferences = new ArrayList(); - public FunctionBlock(AbstractLeekBlock parent, MainLeekBlock main, int line, AIFile ai) { + public FunctionBlock(AbstractLeekBlock parent, MainLeekBlock main, int line, AIFile ai, IAWord token) { super(parent, main, line, ai); + this.token = token; } public int getId() { @@ -25,7 +32,7 @@ public void setId(int id) { } public String getName() { - return mName; + return token.getWord(); } public int countParameters() { @@ -42,30 +49,36 @@ public String referenceArray() { return str + "}"; } - public void setName(String name) { - mName = name; - } - - public void addParameter(String parameter, boolean is_reference) { - mParameters.add(parameter); + public void addParameter(WordCompiler compiler, IAWord parameter, boolean is_reference) { + mParameters.add(parameter.getWord()); mReferences.add(is_reference); - addVariable(parameter); + var declaration = new LeekVariableDeclarationInstruction(compiler, parameter, parameter.getLine(), parameter.getAI(), this); + mParameterDeclarations.add(declaration); + addVariable(new LeekVariable(parameter, VariableType.ARGUMENT, declaration)); } @Override public boolean hasVariable(String variable) { - return mVariables.contains(variable); + return mVariables.containsKey(variable); } @Override public String getCode() { - String str = "function " + mName + "("; + String str = "function " + token.getWord() + "("; for (int i = 0; i < mParameters.size(); i++) { if (i != 0) str += ", "; str += mParameters.get(i); } - return str + "){\n" + super.getCode() + "}\n"; + return str + ") {\n" + super.getCode() + "}\n"; + } + + @Override + public void analyze(WordCompiler compiler) { + var initialFunction = compiler.getCurrentFunction(); + compiler.setCurrentFunction(this); + super.analyze(compiler); + compiler.setCurrentFunction(initialFunction); } @Override @@ -76,29 +89,63 @@ public void checkEndBlock() { @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { StringBuilder sb = new StringBuilder(); - sb.append("private AbstractLeekValue user_function_").append(mName).append("("); + sb.append("private Object f_").append(token.getWord()).append("("); for (int i = 0; i < mParameters.size(); i++) { if (i != 0) sb.append(", "); - sb.append("AbstractLeekValue param_").append(mParameters.get(i)); + sb.append("Object p_").append(mParameters.get(i)); } - sb.append(") throws Exception{"); + sb.append(") throws LeekRunException {"); for (int i = 0; i < mParameters.size(); i++) { - sb.append("final VariableLeekValue user_").append(mParameters.get(i)).append(" = "); - if (mReferences.get(i)) { - sb.append("(param_").append(mParameters.get(i)).append(" instanceof VariableLeekValue)?(VariableLeekValue)param_").append(mParameters.get(i)).append(":"); + var parameter = mParameters.get(i); + var declaration = mParameterDeclarations.get(i); + if (declaration.isCaptured()) { + if (mainblock.getCompiler().getCurrentAI().getVersion() <= 1) { + sb.append("final var u_").append(parameter).append(" = new Wrapper("); + if (mReferences.get(i)) { + sb.append("(p_").append(parameter).append(" instanceof Box) ? (Box) p_").append(parameter).append(" : new Box(" + writer.getAIThis() + ", ").append("p_").append(parameter).append("));"); + } else { + sb.append("new Box(").append(writer.getAIThis()).append(", copy(p_").append(parameter).append(")));"); + } + } else { + sb.append("final var u_").append(parameter).append(" = new Box(").append(writer.getAIThis()).append(", p_").append(parameter).append(");"); + } + } else { + sb.append("var u_").append(parameter).append(" = "); + if (mReferences.get(i)) { + sb.append("(p_").append(parameter).append(" instanceof Box) ? (Box) p_").append(parameter).append(" : new Box(" + writer.getAIThis()); + if (mainblock.getCompiler().getCurrentAI().getVersion() <= 1) { + sb.append(", p_").append(parameter).append(");"); + } else { + sb.append(", copy(p_").append(parameter).append("));"); + } + } else { + if (mainblock.getCompiler().getCurrentAI().getVersion() <= 1) { + sb.append("new Box(" + writer.getAIThis() + ", copy(p_").append(parameter).append("));"); + } else { + sb.append("p_").append(parameter).append(";"); + } + } } - sb.append("new VariableLeekValue(this, param_").append(mParameters.get(i)).append(".getValue());"); } writer.addLine(sb.toString(), mLine, mAI); writer.addCounter(1); super.writeJavaCode(mainblock, writer); if (mEndInstruction == 0) - writer.addLine("return LeekValueManager.NULL;"); + writer.addLine("return null;"); writer.addLine("}"); } public boolean isReference(int i) { return mReferences.get(i); } + + public void declare(WordCompiler compiler) { + // On ajoute la fonction + compiler.getCurrentBlock().addVariable(new LeekVariable(token, VariableType.FUNCTION)); + } + + public String toString() { + return token.getWord(); + } } diff --git a/src/main/java/leekscript/compiler/bloc/MainLeekBlock.java b/src/main/java/leekscript/compiler/bloc/MainLeekBlock.java index 261c7a3a..ace06bea 100644 --- a/src/main/java/leekscript/compiler/bloc/MainLeekBlock.java +++ b/src/main/java/leekscript/compiler/bloc/MainLeekBlock.java @@ -1,35 +1,47 @@ package leekscript.compiler.bloc; +import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.TreeMap; import leekscript.compiler.AIFile; import leekscript.compiler.IACompiler; +import leekscript.compiler.IAWord; import leekscript.compiler.JavaWriter; import leekscript.compiler.LeekScript; import leekscript.compiler.WordCompiler; import leekscript.compiler.WordParser; +import leekscript.compiler.exceptions.LeekCompilerException; +import leekscript.compiler.instruction.ClassDeclarationInstruction; +import leekscript.compiler.instruction.LeekGlobalDeclarationInstruction; import leekscript.compiler.instruction.LeekInstruction; +import leekscript.runner.LeekFunctions; public class MainLeekBlock extends AbstractLeekBlock { - private final ArrayList mGobales = new ArrayList(); - private final ArrayList mGobalesDeclarations = new ArrayList(); + private final ArrayList mGlobales = new ArrayList<>(); + private final ArrayList mGlobalesDeclarations = new ArrayList<>(); private final HashSet mRedefinedFunctions = new HashSet(); private final ArrayList mFunctions = new ArrayList(); private final ArrayList mAnonymousFunctions = new ArrayList(); private final Map mUserFunctions = new TreeMap(); + private final Map mUserClasses = new TreeMap(); + private final List mUserClassesList = new ArrayList<>(); private int mMinLevel = 1; private int mAnonymousId = 1; private int mFunctionId = 1; - private final ArrayList mIncluded = new ArrayList(); + private final ArrayList mIncluded = new ArrayList(); private int mCounter = 0; private int mCountInstruction = 0; private final IACompiler mCompiler; + private String mAIName; + private String className; + private WordCompiler wordCompiler; @Override public int getCount() { @@ -39,9 +51,25 @@ public int getCount() { public MainLeekBlock(IACompiler compiler, AIFile ai) { super(null, null, 0, null); // On ajoute l'IA pour pas pouvoir l'include - mIncluded.add(ai.getPath()); + mIncluded.add(ai.getId()); + mAIName = ai.getPath(); mCompiler = compiler; mCompiler.setCurrentAI(ai); + if (ai.getVersion() >= 3) { + addClass(new ClassDeclarationInstruction(new IAWord("Value"), 0, ai, true)); + addClass(new ClassDeclarationInstruction(new IAWord("Null"), 0, ai, true)); + addClass(new ClassDeclarationInstruction(new IAWord("Boolean"), 0, ai, true)); + addClass(new ClassDeclarationInstruction(new IAWord("Integer"), 0, ai, true)); + addClass(new ClassDeclarationInstruction(new IAWord("Real"), 0, ai, true)); + addClass(new ClassDeclarationInstruction(new IAWord("Number"), 0, ai, true)); + addClass(new ClassDeclarationInstruction(new IAWord("Array"), 0, ai, true)); + addClass(new ClassDeclarationInstruction(new IAWord("String"), 0, ai, true)); + addClass(new ClassDeclarationInstruction(new IAWord("Object"), 0, ai, true)); + addClass(new ClassDeclarationInstruction(new IAWord("Function"), 0, ai, true)); + addClass(new ClassDeclarationInstruction(new IAWord("Class"), 0, ai, true)); + addClass(new ClassDeclarationInstruction(new IAWord("JSON"), 0, ai, true)); + addClass(new ClassDeclarationInstruction(new IAWord("System"), 0, ai, true)); + } } public void addRedefinedFunction(String function) { @@ -68,22 +96,25 @@ public void setMinLevel(int min_level) { this.mMinLevel = min_level; } - public boolean includeAI(String path) throws Exception { - if (mIncluded.contains(path)) { + public boolean includeAI(WordCompiler compiler, String path) throws LeekCompilerException { + try { + AIFile ai = LeekScript.getResolver().resolve(path, mCompiler.getCurrentAI().getContext()); + if (mIncluded.contains(ai.getId())) { + return true; + } + // System.out.println("include " + ai.getPath()); + mIncluded.add(ai.getId()); + AIFile previousAI = mCompiler.getCurrentAI(); + mCompiler.setCurrentAI(ai); + WordParser words = new WordParser(ai, compiler.getVersion()); + WordCompiler newCompiler = new WordCompiler(words, this, ai, compiler.getVersion()); + newCompiler.readCode(); + compiler.addErrors(newCompiler.getErrors()); + mCompiler.setCurrentAI(previousAI); return true; - } - AIFile ai = LeekScript.getResolver().resolve(path, mCompiler.getCurrentAI().getContext()); - if (ai == null) { + } catch (FileNotFoundException e) { return false; } - mIncluded.add(ai.getPath()); - AIFile previousAI = mCompiler.getCurrentAI(); - mCompiler.setCurrentAI(ai); - WordParser words = new WordParser(ai); - WordCompiler compiler = new WordCompiler(words, this, ai); - compiler.readCode(); - mCompiler.setCurrentAI(previousAI); - return true; } public boolean hasUserFunction(String name, boolean use_declarations) { @@ -100,8 +131,8 @@ public void addFunctionDeclaration(String name, int count_param) { mUserFunctions.put(name, count_param); } - public void addGlobalDeclaration(String declaration) { - mGobalesDeclarations.add(declaration); + public void addGlobalDeclaration(LeekGlobalDeclarationInstruction declaration) { + mGlobalesDeclarations.add(declaration); } public void addAnonymousFunction(AnonymousFunctionBlock block) { @@ -116,7 +147,9 @@ public int getUserFunctionParametersCount(String name) { if (block.getName().equals(name)) return block.countParameters(); } - return mUserFunctions.get(name); + var f = mUserFunctions.get(name); + if (f != null) return f; + return -1; } public FunctionBlock getUserFunction(String name) { @@ -129,19 +162,17 @@ public FunctionBlock getUserFunction(String name) { @Override public boolean hasGlobal(String globale) { - if (mGobalesDeclarations.contains(globale)) - return true; - return mGobales.contains(globale); + return mGlobales.contains(globale); } @Override public boolean hasDeclaredGlobal(String globale) { - return mGobales.contains(globale); + return mGlobales.contains(globale); } @Override - public void addGlobal(String globale) { - mGobales.add(globale); + public void addGlobal(String variable) { + mGlobales.add(variable); } public void addFunction(FunctionBlock block) { @@ -156,24 +187,58 @@ public String getCode() { for (LeekInstruction instruction : mFunctions) { str += instruction.getCode() + "\n"; } - for (LeekInstruction instruction : mAnonymousFunctions) { - str += instruction.getCode() + "\n"; + for (var clazz : mUserClasses.values()) { + str += clazz.getCode() + "\n"; } return str + super.getCode(); } public void writeJavaCode(JavaWriter writer, String className, String AIClass) { + this.className = className; + writer.addLine("import leekscript.runner.*;"); writer.addLine("import leekscript.runner.values.*;"); + writer.addLine("import leekscript.common.*;"); + writer.addLine(); writer.addLine("public class " + className + " extends " + AIClass + " {"); - writer.addLine("public " + className + "() throws Exception{ super(); }"); + + // Classes + for (var clazz : mUserClassesList) { + if (clazz.internal) continue; + clazz.declareJava(this, writer); + } + + // Constructor + writer.addLine("public " + className + "() throws LeekRunException {"); + writer.addLine("super(" + mInstructions.size() + ", " + mCompiler.getCurrentAI().getVersion() + ");"); + + for (var clazz : mUserClassesList) { + if (clazz.internal) continue; + clazz.createJava(this, writer); + } + writer.addLine("}"); + + // Static init + writer.addLine("public void staticInit() throws LeekRunException {"); + + // Initialize classes static fields + for (var clazz : mUserClassesList) { + clazz.initializeStaticFields(this, writer); + } + writer.addLine("}"); + // Variables globales - for (String global : mGobales) { - writer.addLine("private VariableLeekValue globale_" + global + " = null;"); + for (String global : mGlobales) { + if (getWordCompiler().getVersion() >= 2) { + writer.addLine("private Object g_" + global + " = null;"); + } else { + writer.addLine("private Box g_" + global + " = new Box(" + writer.getAIThis() + ");"); + } + writer.addLine("private boolean g_init_" + global + " = false;"); } // Fonctions redéfinies for (String redefined : mRedefinedFunctions) { - writer.addCode("private VariableLeekValue rfunction_"); + writer.addCode("private Box rfunction_"); writer.addCode(redefined); writer.addLine(";"); } @@ -185,28 +250,34 @@ public void writeJavaCode(JavaWriter writer, String className, String AIClass) { * for(LeekInstruction instruction : mAnonymousFunctions){ * instruction.writeJavaCode(this, writer); } */ - writer.addLine("public AbstractLeekValue runIA() throws Exception{ resetCounter();"); + writer.addLine("public Object runIA() throws LeekRunException { resetCounter();"); + + for (var clazz : mUserClassesList) { + clazz.writeJavaCode(this, writer); + } + super.writeJavaCode(this, writer); if (mEndInstruction == 0) - writer.addLine("return LeekValueManager.NULL;"); + writer.addLine("return null;"); writer.addLine("}"); - writer.writeErrorFunction(mCompiler, mIncluded.get(0)); + writer.writeErrorFunction(mCompiler, mAIName); printFunctionInformations(writer); if (mRedefinedFunctions.size() > 0) { - writer.addCode("protected void init() throws Exception{"); + writer.addCode("protected void init() throws LeekRunException {\n"); for (String redefined : mRedefinedFunctions) { FunctionBlock user_function = getUserFunction(redefined); writer.addCode("rfunction_"); writer.addCode(redefined); - writer.addCode(" = new VariableLeekValue(mUAI, "); + writer.addCode(" = new Box(" + writer.getAIThis() + ", "); if (user_function != null) { writer.addCode("new FunctionLeekValue("); writer.addCode(String.valueOf(user_function.getId())); writer.addCode(")"); } else { - writer.addCode("new FunctionLeekValue(LeekFunctions."); + String namespace = LeekFunctions.getNamespace(redefined); + writer.addCode("new FunctionLeekValue(" + namespace + "."); writer.addCode(redefined); writer.addCode(")"); } @@ -218,51 +289,63 @@ public void writeJavaCode(JavaWriter writer, String className, String AIClass) { } public void printFunctionInformations(JavaWriter writer) { - // Compteur de parametres - writer.addLine("public int userFunctionCount(int id){"); - writer.addLine("switch(id){"); - for (FunctionBlock f : mFunctions) { - writer.addLine("case " + f.getId() + ": return " + f.countParameters() + ";"); - } - writer.addLine("} return -1; }"); - // Références - writer.addLine("public boolean[] userFunctionReference(int id){"); - writer.addLine("switch(id){"); - for (FunctionBlock f : mFunctions) { - writer.addLine("case " + f.getId() + ": return new boolean[]" + f.referenceArray() + ";"); - } - writer.addLine("} return null; }"); - // Execute - writer.addLine("public AbstractLeekValue userFunctionExecute(int id, AbstractLeekValue[] value) throws Exception{"); - writer.addLine("switch(id){"); - for (FunctionBlock f : mFunctions) { - String params = ""; - for (int i = 0; i < f.countParameters(); i++) { - if (i != 0) - params += ","; - params += "value[" + i + "]"; + if (mFunctions.size() > 0) { + // Compteur de parametres + writer.addLine("public int userFunctionCount(int id) {"); + writer.addLine("switch(id) {"); + for (FunctionBlock f : mFunctions) { + writer.addLine("case " + f.getId() + ": return " + f.countParameters() + ";"); } - writer.addLine("case " + f.getId() + ": return user_function_" + f.getName() + "(" + params + ");"); - } - writer.addLine("} return null; }"); - - writer.addLine("public int anonymousFunctionCount(int id){"); - writer.addLine("switch(id){"); - for (AnonymousFunctionBlock f : mAnonymousFunctions) { - writer.addLine("case " + f.getId() + ": return " + f.countParameters() + ";"); + writer.addLine("} return -1; }"); + writer.addLine(); + + // Références + writer.addLine("public boolean[] userFunctionReference(int id) {"); + writer.addLine("switch(id) {"); + for (FunctionBlock f : mFunctions) { + writer.addLine("case " + f.getId() + ": return new boolean[]" + f.referenceArray() + ";"); + } + writer.addLine("} return null; }"); + writer.addLine(); + + // Execute + writer.addLine("public Object userFunctionExecute(int id, Object[] value) throws LeekRunException {"); + writer.addLine("switch(id) {"); + for (FunctionBlock f : mFunctions) { + String params = ""; + for (int i = 0; i < f.countParameters(); i++) { + if (i != 0) + params += ","; + params += "value[" + i + "]"; + } + writer.addLine("case " + f.getId() + ": return f_" + f.getName() + "(" + params + ");"); + } + writer.addLine("} return null; }"); } - writer.addLine("} return -1; }"); - // Références - writer.addLine("public boolean[] anonymousFunctionReference(int id){"); - writer.addLine("switch(id){"); - for (AnonymousFunctionBlock f : mAnonymousFunctions) { - writer.addLine("case " + f.getId() + ": return new boolean[]" + f.referenceArray() + ";"); + if (mAnonymousFunctions.size() > 0) { + writer.addLine(); + writer.addLine("public int anonymousFunctionCount(int id) {"); + writer.addLine("switch(id) {"); + for (AnonymousFunctionBlock f : mAnonymousFunctions) { + writer.addLine("case " + f.getId() + ": return " + f.countParameters() + ";"); + } + writer.addLine("} return -1; }"); + writer.addLine(); + // Références + writer.addLine("public boolean[] anonymousFunctionReference(int id) {"); + writer.addLine("switch(id){"); + for (AnonymousFunctionBlock f : mAnonymousFunctions) { + writer.addLine("case " + f.getId() + ": return new boolean[]" + f.referenceArray() + ";"); + } + writer.addLine("} return null; }"); } - writer.addLine("} return null; }"); + // writer.addLine("public int getVersion() {"); + // writer.addLine("return " + mCompiler.getCurrentAI().getVersion() + ";"); + // writer.addLine("}"); // Execute /* * writer.addLine( - * "public AbstractLeekValue anonymousFunctionExecute(int id, AbstractLeekValue[] value) throws Exception{" + * "public Object anonymousFunctionExecute(int id, Object[] value) throws Exception{" * ); writer.addLine("switch(id){"); for(AnonymousFunctionBlock f : * mAnonymousFunctions){ String params = ""; for(int i = 0; i < * f.countParameters(); i++){ if(i != 0) params += ","; params += @@ -272,18 +355,70 @@ public void printFunctionInformations(JavaWriter writer) { */ /* * public abstract int userFunctionCount(int id); - * + * * public abstract boolean[] userFunctionReference(int id); - * - * public abstract AbstractLeekValue userFunctionExecute(int id, - * AbstractLeekValue[] value); - * + * + * public abstract Object userFunctionExecute(int id, + * Object[] value); + * * public abstract int anonymousFunctionCount(int id); - * + * * public abstract boolean[] anonymousFunctionReference(int id); - * - * public abstract AbstractLeekValue anonymousFunctionExecute(int id, - * AbstractLeekValue[] value); + * + * public abstract Object anonymousFunctionExecute(int id, + * Object[] value); */ } + + public List getIncludedAIs() { + return mIncluded; + } + + public IACompiler getCompiler() { + return mCompiler; + } + + public boolean hasUserClass(String name) { + return mUserClasses.containsKey(name); + } + + public void addClass(ClassDeclarationInstruction classDeclaration) { + mUserClasses.put(classDeclaration.getName(), classDeclaration); + mUserClassesList.add(classDeclaration); + } + + public ClassDeclarationInstruction getUserClass(String name) { + return mUserClasses.get(name); + } + + public void analyze(WordCompiler compiler) { + for (var clazz : mUserClassesList) { + clazz.declare(compiler); + } + for (var function : mFunctions) { + function.declare(compiler); + } + for (var global : mGlobalesDeclarations) { + global.declare(compiler); + } + for (var clazz : mUserClassesList) { + clazz.analyze(compiler); + } + for (var function : mFunctions) { + function.analyze(compiler); + } + super.analyze(compiler); + } + + public String getClassName() { + return className; + } + + public void setWordCompiler(WordCompiler compiler) { + this.wordCompiler = compiler; + } + + public WordCompiler getWordCompiler() { + return this.wordCompiler; + } } diff --git a/src/main/java/leekscript/compiler/bloc/WhileBlock.java b/src/main/java/leekscript/compiler/bloc/WhileBlock.java index f8a11f8c..5f68c7c3 100644 --- a/src/main/java/leekscript/compiler/bloc/WhileBlock.java +++ b/src/main/java/leekscript/compiler/bloc/WhileBlock.java @@ -2,7 +2,9 @@ import leekscript.compiler.AIFile; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.expression.AbstractExpression; +import leekscript.compiler.expression.LeekBoolean; public class WhileBlock extends AbstractLeekBlock { @@ -27,9 +29,18 @@ public String getCode() { @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - writer.addCode("while("); - mCondition.writeJavaCode(mainblock, writer); - writer.addLine(".getBoolean()){"); + // writer.addCounter(1); + writer.addCode("while (ops("); + // Prevent unreachable code error + if (mCondition instanceof LeekBoolean) { + writer.addCode("bool("); + writer.getBoolean(mainblock, mCondition); + writer.addCode(")"); + } else { + writer.getBoolean(mainblock, mCondition); + } + writer.addCode(", " + (mCondition.getOperations()) + ")"); + writer.addLine(") {"); writer.addCounter(1); super.writeJavaCode(mainblock, writer); writer.addLine("}"); @@ -44,4 +55,11 @@ public boolean isBreakable() { public int getEndBlock() { return 0; } + + public void analyze(WordCompiler compiler) { + if (mCondition != null) { + mCondition.analyze(compiler); + } + super.analyze(compiler); + } } diff --git a/src/main/java/leekscript/compiler/exceptions/LeekCompilerException.java b/src/main/java/leekscript/compiler/exceptions/LeekCompilerException.java index 4cb5f3f9..8f8cfefa 100644 --- a/src/main/java/leekscript/compiler/exceptions/LeekCompilerException.java +++ b/src/main/java/leekscript/compiler/exceptions/LeekCompilerException.java @@ -2,94 +2,45 @@ import leekscript.compiler.AIFile; import leekscript.compiler.IAWord; +import leekscript.common.Error; public class LeekCompilerException extends Exception { - public final static String FUNCTION_NAME_UNAVAILABLE = "function_name_unavailable"; - public final static String PARAMETER_NAME_UNAVAILABLE = "parameter_name_unavailable"; - public final static String OPENING_PARENTHESIS_EXPECTED = "opening_parenthesis_expected"; - public final static String OPENING_CURLY_BRACKET_EXPECTED = "opening_curly_bracket_expected"; - public final static String PARAMETER_NAME_EXPECTED = "parameter_name_expected"; - public final static String FUNCTION_NAME_EXPECTED = "function_name_expected"; - public final static String PARENTHESIS_EXPECTED_AFTER_PARAMETERS = "parenthesis_expected_after_parameters"; - public final static String OPEN_BLOC_REMAINING = "open_bloc_remaining"; - public final static String NO_BLOC_TO_CLOSE = "no_bloc_to_close"; - public final static String END_OF_SCRIPT_UNEXPECTED = "end_of_script_unexpected"; - public final static String END_OF_INSTRUCTION_EXPECTED = "end_of_instruction_expected"; - public final static String BREAK_OUT_OF_LOOP = "break_out_of_loop"; - public final static String CONTINUE_OUT_OF_LOOP = "continue_out_of_loop"; - public final static String INCLUDE_ONLY_IN_MAIN_BLOCK = "include_only_in_main_block"; - public final static String AI_NAME_EXPECTED = "ai_name_expected"; - public final static String AI_NOT_EXISTING = "ai_not_existing"; - public final static String CLOSING_PARENTHESIS_EXPECTED = "closing_parenthesis_expected"; - public final static String CLOSING_SQUARE_BRACKET_EXPECTED = "closing_square_bracket_expected"; - public final static String FUNCTION_ONLY_IN_MAIN_BLOCK = "function_only_in_main_block"; - public final static String VARIABLE_NAME_EXPECTED = "variable_name_expected"; - public final static String VARIABLE_NAME_UNAVAILABLE = "variable_name_unavailable"; - public final static String VARIABLE_NOT_EXISTS = "variable_not_exists"; - public final static String KEYWORD_UNEXPECTED = "keyword_unexpected"; - public final static String KEYWORD_IN_EXPECTED = "keyword_in_expected"; - public final static String WHILE_EXPECTED_AFTER_DO = "while_expected_after_do"; - public final static String NO_IF_BLOCK = "no_if_block"; - public final static String GLOBAL_ONLY_IN_MAIN_BLOCK = "global_only_in_main_block"; - public final static String VAR_NAME_EXPECTED_AFTER_GLOBAL = "var_name_expected_after_global"; - public final static String VAR_NAME_EXPECTED = "var_name_expected"; - public final static String SIMPLE_ARRAY = "simple_array"; - public final static String ASSOCIATIVE_ARRAY = "associative_array"; - public final static String PARENTHESIS_EXPECTED_AFTER_FUNCTION = "parenthesis_expected_after_function"; - public final static String UNKNOWN_VARIABLE_OR_FUNCTION = "unknown_variable_or_function"; - public final static String OPERATOR_UNEXPECTED = "operator_unexpected"; - public final static String VALUE_EXPECTED = "value_expected"; - public final static String CANT_ADD_INSTRUCTION_AFTER_BREAK = "cant_add_instruction_after_break"; - public final static String UNCOMPLETE_EXPRESSION = "uncomplete_expression"; - public final static String CANT_ASSIGN_VALUE = "cant_assign_value"; - public final static String FUNCTION_NOT_EXISTS = "function_not_exists"; - public final static String INVALID_PAREMETER_COUNT = "invalid_parameter_count"; - public final static String INVALID_CHAR = "invalid_char"; - public final static String INVALID_NUMBER = "invalid_number"; - /** - * + * */ private static final long serialVersionUID = 1L; + int mLine; int mChar; String mWord; - String mType; + Error mError; AIFile mIA; private String[] mParameters = null; - public LeekCompilerException(IAWord word) { - mLine = word.getLine(); - mChar = word.getCharacter(); - mWord = word.getWord(); - mIA = word.getAI(); - mType = ""; - } - - public LeekCompilerException(IAWord word, String informations) { + public LeekCompilerException(IAWord word, Error error) { mLine = word.getLine(); mChar = word.getCharacter(); mWord = word.getWord(); mIA = word.getAI(); - mType = informations; + mError = error; } - public LeekCompilerException(IAWord word, String informations, String[] parameters) { + public LeekCompilerException(IAWord word, Error error, String[] parameters) { mLine = word.getLine(); mChar = word.getCharacter(); mWord = word.getWord(); mIA = word.getAI(); - mType = informations; + mError = error; mParameters = parameters; } - public LeekCompilerException(AIFile ai, int line, int char_pos, String word, String informations) { + public LeekCompilerException(AIFile ai, int line, int char_pos, String word, Error error) { mLine = line; mChar = char_pos; mWord = word; mIA = ai; - mType = informations; + mError = error; } public String[] getParameters() { @@ -110,11 +61,11 @@ public int getChar() { @Override public String getMessage() { - return mIA.getPath() + ":" + mLine + " : " + mWord + " : " + mType; + return mIA.getPath() + ":" + mLine + " : " + mWord + " : " + mError.name(); } - public String getError() { - return mType; + public Error getError() { + return mError; } public AIFile getIA() { diff --git a/src/main/java/leekscript/compiler/exceptions/LeekInstructionException.java b/src/main/java/leekscript/compiler/exceptions/LeekInstructionException.java deleted file mode 100644 index 369cce4c..00000000 --- a/src/main/java/leekscript/compiler/exceptions/LeekInstructionException.java +++ /dev/null @@ -1,13 +0,0 @@ -package leekscript.compiler.exceptions; - - -public class LeekInstructionException extends Exception { - /** - * - */ - private static final long serialVersionUID = 1L; - - public LeekInstructionException(String message){ - super(message); - } -} diff --git a/src/main/java/leekscript/compiler/expression/AbstractExpression.java b/src/main/java/leekscript/compiler/expression/AbstractExpression.java index d9d53d5f..14c3fe08 100644 --- a/src/main/java/leekscript/compiler/expression/AbstractExpression.java +++ b/src/main/java/leekscript/compiler/expression/AbstractExpression.java @@ -1,6 +1,8 @@ package leekscript.compiler.expression; +import leekscript.common.Type; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.bloc.MainLeekBlock; public abstract class AbstractExpression { @@ -15,16 +17,144 @@ public abstract class AbstractExpression { public final static int FUNCTION = 8; public final static int GLOBAL = 9; public final static int ARRAY = 10; + public final static int OBJECT = 11; + public final static int OBJECT_ACCESS = 12; - public abstract int getType(); + protected int operations = 0; + + public abstract int getNature(); + + public abstract Type getType(); public abstract String getString(); public abstract void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer); - public abstract boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionException; + public void compileL(MainLeekBlock mainblock, JavaWriter writer) { + writeJavaCode(mainblock, writer); + } + + public void compileSet(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + writeJavaCode(mainblock, writer); + writer.addCode(" = "); + expr.writeJavaCode(mainblock, writer); + } + + public void compileSetCopy(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + writeJavaCode(mainblock, writer); + writer.addCode(" = "); + writer.compileClone(mainblock, expr); + } + + public void compileIncrement(MainLeekBlock mainblock, JavaWriter writer) { + writeJavaCode(mainblock, writer); + writer.addCode("++"); + } + + public void compileDecrement(MainLeekBlock mainblock, JavaWriter writer) { + writeJavaCode(mainblock, writer); + writer.addCode("--"); + } + + public void compilePreIncrement(MainLeekBlock mainblock, JavaWriter writer) { + writer.addCode("++"); + writeJavaCode(mainblock, writer); + } + + public void compilePreDecrement(MainLeekBlock mainblock, JavaWriter writer) { + writer.addCode("--"); + writeJavaCode(mainblock, writer); + } + + public void compileAddEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + writeJavaCode(mainblock, writer); + writer.addCode(" += "); + expr.writeJavaCode(mainblock, writer); + } + + public void compileSubEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + writeJavaCode(mainblock, writer); + writer.addCode(" -= "); + expr.writeJavaCode(mainblock, writer); + } + + public void compileMulEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + writeJavaCode(mainblock, writer); + writer.addCode(" *= "); + expr.writeJavaCode(mainblock, writer); + } + + public void compileDivEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + writeJavaCode(mainblock, writer); + writer.addCode(" /= "); + expr.writeJavaCode(mainblock, writer); + } + + public void compilePowEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + writeJavaCode(mainblock, writer); + writer.addCode(" **= "); + expr.writeJavaCode(mainblock, writer); + } + + public void compileModEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + writeJavaCode(mainblock, writer); + writer.addCode(" %= "); + expr.writeJavaCode(mainblock, writer); + } + + public void compileBitOrEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + writeJavaCode(mainblock, writer); + writer.addCode(" |= "); + expr.writeJavaCode(mainblock, writer); + } + + public void compileBitAndEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + writeJavaCode(mainblock, writer); + writer.addCode(" &= "); + expr.writeJavaCode(mainblock, writer); + } + + public void compileBitXorEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + writeJavaCode(mainblock, writer); + writer.addCode(" ^= "); + expr.writeJavaCode(mainblock, writer); + } + + public void compileShiftLeftEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + writeJavaCode(mainblock, writer); + writer.addCode(" <<= "); + expr.writeJavaCode(mainblock, writer); + } + + public void compileShiftRightEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + writeJavaCode(mainblock, writer); + writer.addCode(" >>= "); + expr.writeJavaCode(mainblock, writer); + } + + public void compileShiftUnsignedRightEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + writeJavaCode(mainblock, writer); + writer.addCode(" >>>= "); + expr.writeJavaCode(mainblock, writer); + } + + public abstract boolean validExpression(WordCompiler compiler, MainLeekBlock mainblock) throws LeekExpressionException; public AbstractExpression trim() { return this; } + + public boolean isLeftValue() { + return false; + } + + public boolean nullable() { + return true; + } + + public abstract void analyze(WordCompiler compiler); + + public int getOperations() { + return operations; + } } diff --git a/src/main/java/leekscript/compiler/expression/LeekAnonymousFunction.java b/src/main/java/leekscript/compiler/expression/LeekAnonymousFunction.java index 6ca99820..54d86331 100644 --- a/src/main/java/leekscript/compiler/expression/LeekAnonymousFunction.java +++ b/src/main/java/leekscript/compiler/expression/LeekAnonymousFunction.java @@ -1,6 +1,8 @@ package leekscript.compiler.expression; +import leekscript.common.Type; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.bloc.AnonymousFunctionBlock; import leekscript.compiler.bloc.MainLeekBlock; @@ -13,17 +15,22 @@ public LeekAnonymousFunction(AnonymousFunctionBlock block) { } @Override - public int getType() { + public int getNature() { return FUNCTION; } + @Override + public Type getType() { + return Type.FUNCTION; + } + @Override public String getString() { - return "#Anonymous" + mBlock.getId(); + return mBlock.getCode(); } @Override - public boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionException { + public boolean validExpression(WordCompiler compiler, MainLeekBlock mainblock) throws LeekExpressionException { return true; } @@ -34,4 +41,11 @@ public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { writer.addCode(")"); } + @Override + public void analyze(WordCompiler compiler) { + var previousFunction = compiler.getCurrentFunction(); + compiler.setCurrentFunction(mBlock); + mBlock.analyze(compiler); + compiler.setCurrentFunction(previousFunction); + } } diff --git a/src/main/java/leekscript/compiler/expression/LeekArray.java b/src/main/java/leekscript/compiler/expression/LeekArray.java index 7b5a6344..8b6f29c2 100644 --- a/src/main/java/leekscript/compiler/expression/LeekArray.java +++ b/src/main/java/leekscript/compiler/expression/LeekArray.java @@ -2,7 +2,9 @@ import java.util.ArrayList; +import leekscript.common.Type; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.bloc.MainLeekBlock; public class LeekArray extends AbstractExpression { @@ -22,38 +24,56 @@ public void addValue(AbstractExpression key, AbstractExpression value) { } @Override - public int getType() { + public int getNature() { return ARRAY; } + @Override + public Type getType() { + return Type.ARRAY; + } + @Override public String getString() { String str = "["; for(int i = 0; i < mValues.size(); i++){ - if(i > 0) str += ", "; + if (i > 0) str += ", "; + if (mIsKeyVal) { + str += mValues.get(i).getString() + ": "; + i++; + } str += mValues.get(i).getString(); } return str + "]"; } @Override - public boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionException { + public boolean validExpression(WordCompiler compiler, MainLeekBlock mainblock) throws LeekExpressionException { for(AbstractExpression parameter : mValues){ - parameter.validExpression(mainblock); + parameter.validExpression(compiler, mainblock); } return true; } @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - if(mValues.size() == 0) writer.addCode("new ArrayLeekValue()"); - else{ - writer.addCode("new ArrayLeekValue(mUAI, new AbstractLeekValue[]{"); - for(int i = 0; i < mValues.size(); i++){ - if(i != 0) writer.addCode(","); + if (mValues.size() == 0) writer.addCode("new ArrayLeekValue()"); + else { + writer.addCode("new ArrayLeekValue(" + 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("}, " + (mIsKeyVal ? "true" : "false") + ")"); + writer.addCode(" }, " + (mIsKeyVal ? "true" : "false") + ")"); + } + } + + @Override + public void analyze(WordCompiler compiler) { + operations = 0; + for (var value : mValues) { + value.analyze(compiler); + operations += value.getOperations(); } } } diff --git a/src/main/java/leekscript/compiler/expression/LeekBoolean.java b/src/main/java/leekscript/compiler/expression/LeekBoolean.java index 74df9a88..000ae89f 100644 --- a/src/main/java/leekscript/compiler/expression/LeekBoolean.java +++ b/src/main/java/leekscript/compiler/expression/LeekBoolean.java @@ -1,6 +1,8 @@ package leekscript.compiler.expression; +import leekscript.common.Type; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.bloc.MainLeekBlock; public class LeekBoolean extends AbstractExpression { @@ -12,10 +14,15 @@ public LeekBoolean(boolean value) { } @Override - public int getType() { + public int getNature() { return BOOLEAN; } + @Override + public Type getType() { + return Type.BOOL; + } + @Override public String getString() { return mValue ? "true" : "false"; @@ -23,12 +30,16 @@ public String getString() { @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - writer.addCode("LeekValueManager." + (mValue ? "TRUE" : "FALSE")); + writer.addCode(mValue ? "true" : "false"); } @Override - public boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionException { + public boolean validExpression(WordCompiler compiler, MainLeekBlock mainblock) throws LeekExpressionException { return true; } + @Override + public void analyze(WordCompiler compiler) { + + } } diff --git a/src/main/java/leekscript/compiler/expression/LeekConstant.java b/src/main/java/leekscript/compiler/expression/LeekConstant.java deleted file mode 100644 index f1d1a026..00000000 --- a/src/main/java/leekscript/compiler/expression/LeekConstant.java +++ /dev/null @@ -1,41 +0,0 @@ -package leekscript.compiler.expression; - -import leekscript.compiler.JavaWriter; -import leekscript.compiler.bloc.MainLeekBlock; -import leekscript.runner.ILeekConstant; -import leekscript.runner.LeekFunctions; - -public class LeekConstant extends AbstractExpression { - - private final String mConstantName; - private final ILeekConstant mConstant; - - public LeekConstant(String word, ILeekConstant constant) { - mConstantName = word; - mConstant = constant; - } - - @Override - public int getType() { - return 0; - } - - @Override - public String getString() { - return mConstantName; - } - - @Override - public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - if (mConstant.getType() == LeekFunctions.INT) writer.addCode("LeekValueManager.getLeekIntValue(" + mConstant.getIntValue() + ")"); - else if (mConstant.getType() == LeekFunctions.DOUBLE) writer.addCode("new DoubleLeekValue(" + mConstant.getValue() + ")"); - else writer.addCode("LeekValueManager.NULL"); - } - - @Override - public boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionException { - // La vérification se fait en amont - return true; - } - -} diff --git a/src/main/java/leekscript/compiler/expression/LeekExpression.java b/src/main/java/leekscript/compiler/expression/LeekExpression.java index dcc6c012..8f7b719f 100644 --- a/src/main/java/leekscript/compiler/expression/LeekExpression.java +++ b/src/main/java/leekscript/compiler/expression/LeekExpression.java @@ -1,25 +1,41 @@ package leekscript.compiler.expression; +import leekscript.compiler.AnalyzeError; +import leekscript.compiler.IAWord; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; +import leekscript.compiler.AnalyzeError.AnalyzeErrorLevel; import leekscript.compiler.bloc.MainLeekBlock; -import leekscript.compiler.exceptions.LeekCompilerException; +import leekscript.compiler.expression.LeekVariable.VariableType; +import leekscript.runner.values.LeekValue; +import leekscript.common.Error; +import leekscript.common.Type; public class LeekExpression extends AbstractExpression { private int mOperator = -1; + private IAWord mOperatorToken; protected AbstractExpression mExpression1 = null; protected AbstractExpression mExpression2 = null; - protected LeekExpression mParent = null; + private Type type = Type.ANY; + + public LeekExpression() {} + + public LeekExpression(AbstractExpression x, int operator, AbstractExpression y) { + mExpression1 = x; + mOperator = operator; + mExpression2 = y; + } public void setParent(LeekExpression parent) { mParent = parent; } public boolean hasTernaire() { - if (mExpression1 != null && mExpression1.getType() == EXPRESSION && ((LeekExpression) mExpression1).hasTernaire()) + if (mExpression1 != null && mExpression1.getNature() == EXPRESSION && ((LeekExpression) mExpression1).hasTernaire()) return true; - if (mExpression2 != null && mExpression2.getType() == EXPRESSION && ((LeekExpression) mExpression2).hasTernaire()) + if (mExpression2 != null && mExpression2.getNature() == EXPRESSION && ((LeekExpression) mExpression2).hasTernaire()) return true; return false; } @@ -48,19 +64,47 @@ public int getOperator() { return mOperator; } - public void setOperator(int operator) { + public IAWord getOperatorToken() { + return mOperatorToken; + } + + public int getLastOperator() { + if (mExpression2 != null && mExpression2 instanceof LeekExpression) { + return ((LeekExpression) mExpression2).getLastOperator(); + } + return mOperator; + } + + public AbstractExpression getLastExpression() { + if (mExpression2 != null) { + if (mExpression2 instanceof LeekExpression) { + return ((LeekExpression) mExpression2).getLastExpression(); + } + return mExpression2; + } + if (mExpression1 != null) { + if (mExpression1 instanceof LeekExpression) { + return ((LeekExpression) mExpression1).getLastExpression(); + } + return mExpression1; + } + return mExpression1; + } + + public void setOperator(int operator, IAWord operatorToken) { mOperator = operator; + mOperatorToken = operatorToken; } public boolean needOperator() { if (mExpression1 != null) { - if (mExpression1.getType() == EXPRESSION && !((LeekExpression) mExpression1).complete()) + if (mExpression1.getNature() == EXPRESSION && !((LeekExpression) mExpression1).complete()) return ((LeekExpression) mExpression1).needOperator(); if (mOperator == -1) return true; } if (mExpression2 != null) { - if (mExpression2.getType() == EXPRESSION) + if (mExpression2.getNature() == EXPRESSION) return ((LeekExpression) mExpression2).needOperator(); return true; } @@ -68,17 +112,22 @@ public boolean needOperator() { } public LeekExpression lastExpression() { - if (mExpression2 != null && mExpression2.getType() == EXPRESSION) + if (mExpression2 != null && mExpression2.getNature() == EXPRESSION) return ((LeekExpression) mExpression2).lastExpression(); else return this; } @Override - public int getType() { + public int getNature() { return AbstractExpression.EXPRESSION; } + @Override + public Type getType() { + return type; + } + public boolean complete(int operator) { return complete(); } @@ -86,9 +135,9 @@ public boolean complete(int operator) { public boolean complete() { if (mExpression1 == null || mExpression2 == null) return false; - if (mExpression1.getType() == EXPRESSION && !((LeekExpression) mExpression1).complete()) + if (mExpression1.getNature() == EXPRESSION && !((LeekExpression) mExpression1).complete()) return false; - if (mExpression2.getType() == EXPRESSION && !((LeekExpression) mExpression2).complete()) + if (mExpression2.getNature() == EXPRESSION && !((LeekExpression) mExpression2).complete()) return false; return true; } @@ -96,7 +145,7 @@ public boolean complete() { public void addExpression(AbstractExpression expression) { if (mExpression1 == null) mExpression1 = expression; - else if (mExpression1.getType() == EXPRESSION && !((LeekExpression) mExpression1).complete()) { + else if (mExpression1.getNature() == EXPRESSION && !((LeekExpression) mExpression1).complete()) { ((LeekExpression) mExpression1).addExpression(expression); } else { @@ -107,12 +156,12 @@ else if (mExpression1.getType() == EXPRESSION && !((LeekExpression) mExpression1 } } - public void addUnaryPrefix(int operator) { + public void addUnaryPrefix(int operator, IAWord operatorToken) { // On doit trouver à quel endroit de l'arborescence on doit placer le // préfix // En général c'est un => ! LeekExpression exp = new LeekExpression(); - exp.setOperator(operator); + exp.setOperator(operator, operatorToken); exp.setParent(this); exp.setExpression1(new LeekNull()); addExpression(exp); @@ -121,7 +170,7 @@ public void addUnaryPrefix(int operator) { public void addBracket(AbstractExpression casevalue) { // On doit ajouter ce crochet au dernier élément ajouté if (mExpression1 != null && mExpression2 == null) { - if (mExpression1.getType() == EXPRESSION) + if (mExpression1.getNature() == EXPRESSION) ((LeekExpression) mExpression1).addBracket(casevalue); else { // On doit ajouter à l'élément mExpression1 @@ -132,7 +181,7 @@ public void addBracket(AbstractExpression casevalue) { } } else if (mExpression2 != null) { - if (mExpression2.getType() == EXPRESSION) + if (mExpression2.getNature() == EXPRESSION) ((LeekExpression) mExpression2).addBracket(casevalue); else { // On doit ajouter à l'élément mExpression2 @@ -144,10 +193,26 @@ else if (mExpression2 != null) { } } + public void addObjectAccess(IAWord name) { + if (mExpression1 != null && mExpression2 == null) { + if (mExpression1.getNature() == EXPRESSION) + ((LeekExpression) mExpression1).addObjectAccess(name); + else { + mExpression1 = new LeekObjectAccess(mExpression1, name); + } + } else if (mExpression2 != null) { + if (mExpression2.getNature() == EXPRESSION) + ((LeekExpression) mExpression2).addObjectAccess(name); + else { + mExpression2 = new LeekObjectAccess(mExpression2, name); + } + } + } + public void addFunction(LeekExpressionFunction function) { // On doit ajouter ce crochet au dernier élément ajouté if (mExpression1 != null && mExpression2 == null) { - if (mExpression1.getType() == EXPRESSION) + if (mExpression1.getNature() == EXPRESSION) ((LeekExpression) mExpression1).addFunction(function); else { // On doit ajouter à l'élément mExpression1 @@ -156,7 +221,7 @@ public void addFunction(LeekExpressionFunction function) { } } else if (mExpression2 != null) { - if (mExpression2.getType() == EXPRESSION) + if (mExpression2.getNature() == EXPRESSION) ((LeekExpression) mExpression2).addFunction(function); else { // On doit ajouter à l'élément mExpression2 @@ -166,30 +231,30 @@ else if (mExpression2 != null) { } } - public void addUnarySuffix(int suffix) { + public void addUnarySuffix(int suffix, IAWord token) { // On doit ajouter ce suffix au dernier élément ajouté if (mExpression1 != null && mExpression2 == null) { - if (mExpression1.getType() == EXPRESSION) - ((LeekExpression) mExpression1).addUnarySuffix(suffix); + if (mExpression1.getNature() == EXPRESSION) + ((LeekExpression) mExpression1).addUnarySuffix(suffix, token); else { // On doit ajouter à l'élément mExpression1 LeekExpression exp = new LeekExpression(); exp.setParent(this); exp.setExpression1(new LeekNull()); - exp.setOperator(suffix); + exp.setOperator(suffix, token); exp.setExpression2(mExpression1); mExpression1 = exp; } } else if (mExpression2 != null) { - if (mExpression2.getType() == EXPRESSION) - ((LeekExpression) mExpression2).addUnarySuffix(suffix); + if (mExpression2.getNature() == EXPRESSION) + ((LeekExpression) mExpression2).addUnarySuffix(suffix, token); else { // On doit ajouter à l'élément mExpression2 LeekExpression exp = new LeekExpression(); exp.setParent(this); exp.setExpression1(new LeekNull()); - exp.setOperator(suffix); + exp.setOperator(suffix, token); exp.setExpression2(mExpression2); mExpression2 = exp; } @@ -202,7 +267,7 @@ public void addTernaire() { if (mExpression1 != null) { LeekTernaire ternaire = new LeekTernaire(); ternaire.addExpression(mExpression1); - ternaire.addOperator(Operators.TERNAIRE); + ternaire.addOperator(Operators.TERNAIRE, null); ternaire.setParent(this); mExpression1 = ternaire; } @@ -217,44 +282,44 @@ public void addTernaire() { new_e.setParent(this); new_e.setExpression1(mExpression1); new_e.setExpression2(mExpression2); - new_e.setOperator(mOperator); + new_e.setOperator(mOperator, null); // Et la mettre en condition de ternaire LeekTernaire ternaire = new LeekTernaire(); ternaire.addExpression(new_e); - ternaire.addOperator(Operators.TERNAIRE); + ternaire.addOperator(Operators.TERNAIRE, null); ternaire.setParent(this); mExpression1 = ternaire; mExpression2 = null; mOperator = -1; } - else if (mExpression2.getType() != EXPRESSION) { + else if (mExpression2.getNature() != EXPRESSION) { // On doit englober l'expression de droite LeekTernaire ternaire = new LeekTernaire(); ternaire.addExpression(mExpression2); - ternaire.addOperator(Operators.TERNAIRE); + ternaire.addOperator(Operators.TERNAIRE, null); ternaire.setParent(this); mExpression2 = ternaire; } else - ((LeekExpression) mExpression2).addOperator(operator); + ((LeekExpression) mExpression2).addOperator(operator, null); } } public void replaceExpression(AbstractExpression base, AbstractExpression replacement) { if (mExpression1 == base) { - if (replacement.getType() == EXPRESSION) + if (replacement.getNature() == EXPRESSION) ((LeekExpression) replacement).setParent(this); mExpression1 = replacement; } else if (mExpression2 == base) { - if (replacement.getType() == EXPRESSION) + if (replacement.getNature() == EXPRESSION) ((LeekExpression) replacement).setParent(this); mExpression2 = replacement; } } - public void addOperator(int operator) { + public void addOperator(int operator, IAWord token) { // On doit trouver à quel endroit de l'arborescence on doit placer // l'opérateur /* @@ -265,16 +330,16 @@ public void addOperator(int operator) { * mExpression1.getType() == EXPRESSION) ((LeekExpression) * mExpression1).addOperator(operator); } else */ - if (mExpression1 != null && mExpression1.getType() == EXPRESSION && !((LeekExpression) mExpression1).complete(operator)) { - ((LeekExpression) mExpression1).addOperator(operator); + if (mExpression1 != null && mExpression1.getNature() == EXPRESSION && !((LeekExpression) mExpression1).complete(operator)) { + ((LeekExpression) mExpression1).addOperator(operator, token); } else if (mOperator == -1) { if (operator == Operators.TERNAIRE) { LeekTernaire trn = new LeekTernaire(); - if (mExpression1.getType() == EXPRESSION) + if (mExpression1.getNature() == EXPRESSION) ((LeekExpression) mExpression1).setParent(trn); trn.addExpression(mExpression1); - trn.addOperator(operator); + trn.addOperator(operator, token); if (mParent != null) mParent.replaceExpression(this, trn); else { @@ -282,8 +347,10 @@ else if (mOperator == -1) { mExpression1 = trn; } } - else + else { mOperator = operator; + mOperatorToken = token; + } } else { int cur_p = Operators.getPriority(mOperator); @@ -294,13 +361,13 @@ else if (mOperator == -1) { new_e.setParent(this); new_e.setExpression1(mExpression1); new_e.setExpression2(mExpression2); - new_e.setOperator(mOperator); + new_e.setOperator(mOperator, token); if (operator == Operators.TERNAIRE) { LeekTernaire trn = new LeekTernaire(); - if (mExpression1.getType() == EXPRESSION) + if (mExpression1.getNature() == EXPRESSION) ((LeekExpression) mExpression1).setParent(trn); trn.addExpression(new_e); - trn.addOperator(operator); + trn.addOperator(operator, token); if (mParent != null) mParent.replaceExpression(this, trn); else { @@ -316,142 +383,155 @@ else if (mOperator == -1) { mOperator = operator; } } - else if (mExpression2.getType() != EXPRESSION) { + else if (mExpression2.getNature() != EXPRESSION) { // On doit englober l'expression de droite if (operator == Operators.TERNAIRE) { LeekTernaire trn = new LeekTernaire(); trn.setParent(this); trn.addExpression(mExpression2); - trn.addOperator(operator); + trn.addOperator(operator, token); mExpression2 = trn; } else { LeekExpression new_e = new LeekExpression(); new_e.setParent(this); new_e.setExpression1(mExpression2); - new_e.setOperator(operator); + new_e.setOperator(operator, token); mExpression2 = new_e; } } else - ((LeekExpression) mExpression2).addOperator(operator); + ((LeekExpression) mExpression2).addOperator(operator, token); } } @Override public String getString() { - String retour = "("; + String retour = ""; if (Operators.isUnaryPrefix(mOperator)) { - ; - retour += Operators.getString(mOperator); + retour += Operators.getString(mOperator, mOperatorToken.getAI().getVersion()); + if (mOperator == Operators.NEW) retour += " "; retour += mExpression2 == null ? "null" : mExpression2.getString(); } else if (Operators.isUnarySuffix(mOperator)) { retour += mExpression2 == null ? "null" : mExpression2.getString(); - retour += Operators.getString(mOperator); + retour += Operators.getString(mOperator, mOperatorToken.getAI().getVersion()); } else { retour += mExpression1 == null ? "null" : mExpression1.getString(); - retour += " " + Operators.getString(mOperator) + " "; + retour += " " + Operators.getString(mOperator, mOperatorToken.getAI().getVersion()) + " "; retour += mExpression2 == null ? "null" : mExpression2.getString(); } - return retour + ")"; - } - - public AbstractExpression getAbstractExpression() { - // Retourner l'AbstractExpression (dans le cas où on n'aurait pas - // d'expression complete) - if (mOperator == -1) - return mExpression1; - return this; + return retour + ""; } @Override - public boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionException { - if (mExpression1 instanceof LeekExpression) - mExpression1 = ((LeekExpression) mExpression1).getAbstractExpression(); - if (mExpression2 instanceof LeekExpression) - mExpression2 = ((LeekExpression) mExpression2).getAbstractExpression(); + public boolean validExpression(WordCompiler compiler, MainLeekBlock mainblock) throws LeekExpressionException { + // if (mExpression1 instanceof LeekExpression) + // mExpression1 = ((LeekExpression) mExpression1).getAbstractExpression(); + // if (mExpression2 instanceof LeekExpression) + // mExpression2 = ((LeekExpression) mExpression2).getAbstractExpression(); if (mExpression1 == null || mExpression2 == null || mOperator == -1) - throw new LeekExpressionException(this, LeekCompilerException.UNCOMPLETE_EXPRESSION); - - // Si on a affaire à une assignation, incrémentation ou autre du genre - // on doit vérifier qu'on a bien une variable (l-value) - if (mOperator == Operators.ADDASSIGN || mOperator == Operators.MINUSASSIGN || mOperator == Operators.DIVIDEASSIGN || mOperator == Operators.ASSIGN || mOperator == Operators.MODULUSASSIGN - || mOperator == Operators.MULTIPLIEASSIGN || mOperator == Operators.POWERASSIGN) { - if (!(mExpression1 instanceof LeekFunctionValue) && !(mExpression1 instanceof LeekVariable) && !(mExpression1 instanceof LeekGlobal) && !(mExpression1 instanceof LeekTabularValue)) - throw new LeekExpressionException(mExpression1, LeekCompilerException.CANT_ASSIGN_VALUE); - if (mExpression1 instanceof LeekFunctionValue) - mainblock.addRedefinedFunction(((LeekFunctionValue) mExpression1).getFunctionName()); - if (mExpression1 instanceof LeekTabularValue) - ((LeekTabularValue) mExpression1).setLeftValue(true); - } - if (mOperator == Operators.INCREMENT || mOperator == Operators.DECREMENT || mOperator == Operators.PRE_INCREMENT || mOperator == Operators.PRE_DECREMENT) { - if (!(mExpression2 instanceof LeekFunctionValue) && !(mExpression2 instanceof LeekVariable) && !(mExpression2 instanceof LeekGlobal) && !(mExpression2 instanceof LeekTabularValue)) - throw new LeekExpressionException(mExpression2, LeekCompilerException.CANT_ASSIGN_VALUE); - if (mExpression2 instanceof LeekFunctionValue) - mainblock.addRedefinedFunction(((LeekFunctionValue) mExpression2).getFunctionName()); - if (mExpression2 instanceof LeekTabularValue) - ((LeekTabularValue) mExpression2).setLeftValue(true); - } - - return mExpression1.validExpression(mainblock) && mExpression2.validExpression(mainblock); + throw new LeekExpressionException(this, Error.UNCOMPLETE_EXPRESSION); + return mExpression1.validExpression(compiler, mainblock) && mExpression2.validExpression(compiler, mainblock); } @Override public AbstractExpression trim() { if (mExpression2 == null) return mExpression1.trim(); + // if (mOperator == Operators.REFERENCE) { + // return mExpression1.trim(); + // } return this; } @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - if (mExpression1 instanceof LeekExpression) - mExpression1 = ((LeekExpression) mExpression1).getAbstractExpression(); - if (mExpression2 instanceof LeekExpression) - mExpression2 = ((LeekExpression) mExpression2).getAbstractExpression(); + // Retourner le code java de l'expression... plein de cas :) switch (mOperator) { // Les classiques - case Operators.ADD:// Addition (on commence facile) - writer.addCode("LeekOperations.add(mUAI, "); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(", "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + case Operators.ADD: // Addition (on commence facile) + if (mExpression1.getType().isNumber() && mExpression2.getType().isNumber()) { + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(" + "); + mExpression2.writeJavaCode(mainblock, writer); + // } else if (mExpression1.getType() == Type.STRING || mExpression2.getType() == Type.STRING) { + // if (mExpression1.getType() == Type.STRING) + // mExpression1.writeJavaCode(mainblock, writer); + // else + // writer.getString(mainblock, mExpression1); + // writer.addCode(" + "); + // if (mExpression2.getType() == Type.STRING || mExpression2.getType() == Type.REAL || mExpression2.getType() == Type.INT || mExpression2.getType() == Type.BOOL) + // mExpression2.writeJavaCode(mainblock, writer); + // else + // writer.getString(mainblock, mExpression2); + } else { + writer.addCode("add("); + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mExpression2.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } return; - case Operators.MINUS:// Soustraction - writer.addCode("LeekOperations.minus(mUAI, "); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(", "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + case Operators.MINUS: // Soustraction + if (mExpression1.getType().isNumber() && mExpression2.getType().isNumber()) { + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(" - "); + mExpression2.writeJavaCode(mainblock, writer); + } else { + writer.addCode("sub("); + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mExpression2.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } return; - case Operators.MULTIPLIE:// Multiplication - writer.addCode("LeekOperations.multiply(mUAI, "); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(", "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + case Operators.MULTIPLIE: // Multiplication + if (mExpression1.getType().isNumber() && mExpression2.getType().isNumber()) { + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(" * "); + mExpression2.writeJavaCode(mainblock, writer); + } else { + writer.addCode("mul("); + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mExpression2.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } return; case Operators.MODULUS:// Modulo - writer.addCode("LeekOperations.modulus(mUAI, "); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(", "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + if (mExpression1.getType().isNumber() && mExpression2.getType().isNumber()) { + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(" % "); + mExpression2.writeJavaCode(mainblock, writer); + } else { + writer.addCode("mod("); + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mExpression2.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } return; case Operators.DIVIDE:// Division - writer.addCode("LeekOperations.divide(mUAI, "); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(", "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); - return; - case Operators.POWER:// Puissance - writer.addCode("LeekOperations.power(mUAI, "); + // Division by zero is not handled + // if (mExpression1.getType().isNumber() && mExpression2.getType().isNumber()) { + // mExpression1.writeJavaCode(mainblock, writer); + // writer.addCode(" / "); + // mExpression2.writeJavaCode(mainblock, writer); + // } else { + writer.addCode("div("); + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(", "); + writer.compileLoad(mainblock, mExpression2); + // mExpression2.writeJavaCode(mainblock, writer); + writer.addCode(")"); + // } + return; + case Operators.POWER: // Puissance + writer.addCode("pow("); mExpression1.writeJavaCode(mainblock, writer); writer.addCode(", "); mExpression2.writeJavaCode(mainblock, writer); @@ -459,234 +539,361 @@ public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { return; // Les binaires case Operators.BITAND: - writer.addCode("LeekOperations.band(mUAI, "); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(", "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + writer.getInt(mainblock, mExpression1); + writer.addCode(" & "); + writer.getInt(mainblock, mExpression2); return; case Operators.BITOR: - writer.addCode("LeekOperations.bor(mUAI, "); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(", "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + writer.getInt(mainblock, mExpression1); + writer.addCode(" | "); + writer.getInt(mainblock, mExpression2); return; case Operators.BITXOR: - writer.addCode("LeekOperations.bxor(mUAI, "); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(", "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + writer.getInt(mainblock, mExpression1); + writer.addCode(" ^ "); + writer.getInt(mainblock, mExpression2); return; case Operators.SHIFT_LEFT: - writer.addCode("LeekOperations.bleft(mUAI, "); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(", "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + writer.getInt(mainblock, mExpression1); + writer.addCode(" << "); + writer.getInt(mainblock, mExpression2); return; case Operators.SHIFT_RIGHT: - writer.addCode("LeekOperations.bright(mUAI, "); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(", "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + writer.getInt(mainblock, mExpression1); + writer.addCode(" >> "); + writer.getInt(mainblock, mExpression2); return; - case Operators.ROTATE_RIGHT: - writer.addCode("LeekOperations.brotate(mUAI, "); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(", "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + case Operators.SHIFT_UNSIGNED_RIGHT: + writer.getInt(mainblock, mExpression1); + writer.addCode(" >>> "); + writer.getInt(mainblock, mExpression2); return; // Les logiques case Operators.EQUALS_EQUALS: - writer.addCode("LeekOperations.equals_equals(mUAI, "); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(", "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + if (mExpression2 instanceof LeekNull) { + if (mExpression1.getType() == Type.BOOL || mExpression1.getType().isNumber()) { + writer.addCode("false"); + } else { + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(" == null"); + } + } else { + writer.addCode("equals_equals("); + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mExpression2.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } return; case Operators.NOT_EQUALS_EQUALS: - writer.addCode("LeekOperations.notequals_equals(mUAI, "); + writer.addCode("notequals_equals("); mExpression1.writeJavaCode(mainblock, writer); writer.addCode(", "); mExpression2.writeJavaCode(mainblock, writer); writer.addCode(")"); return; case Operators.EQUALS: - writer.addCode("LeekOperations.equals(mUAI, "); + writer.addCode("eq("); mExpression1.writeJavaCode(mainblock, writer); writer.addCode(", "); mExpression2.writeJavaCode(mainblock, writer); writer.addCode(")"); return; case Operators.MORE: - writer.addCode("LeekOperations.more(mUAI, "); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(", "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + if (mExpression1.getType().isNumber() && mExpression2.getType().isNumber()) { + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(" > "); + mExpression2.writeJavaCode(mainblock, writer); + } else { + writer.addCode("more("); + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mExpression2.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } return; case Operators.LESS: - writer.addCode("LeekOperations.less(mUAI, "); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(", "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + if (mExpression1.getType().isNumber() && mExpression2.getType().isNumber()) { + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(" < "); + mExpression2.writeJavaCode(mainblock, writer); + } else { + writer.addCode("less("); + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mExpression2.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } return; case Operators.MOREEQUALS: - writer.addCode("LeekOperations.moreequals(mUAI, "); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(", "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + if (mExpression1.getType().isNumber() && mExpression2.getType().isNumber()) { + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(" >= "); + mExpression2.writeJavaCode(mainblock, writer); + } else { + writer.addCode("moreequals("); + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mExpression2.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } return; case Operators.LESSEQUALS: - writer.addCode("LeekOperations.lessequals(mUAI, "); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(", "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + if (mExpression1.getType().isNumber() && mExpression2.getType().isNumber()) { + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(" <= "); + mExpression2.writeJavaCode(mainblock, writer); + } else { + writer.addCode("lessequals("); + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mExpression2.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } return; case Operators.NOTEQUALS: - writer.addCode("LeekOperations.notequals(mUAI, "); + writer.addCode("neq("); mExpression1.writeJavaCode(mainblock, writer); writer.addCode(", "); mExpression2.writeJavaCode(mainblock, writer); writer.addCode(")"); return; case Operators.AND: - writer.addCode("LeekValueManager.getLeekBooleanValue("); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(".getBoolean() && "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(".getBoolean())"); + writer.addCode("("); + writer.addCode("ops("); + writer.getBoolean(mainblock, mExpression1); + writer.addCode(", " + mExpression1.operations + ") && ops("); + writer.getBoolean(mainblock, mExpression2); + writer.addCode(", " + mExpression2.operations + "))"); return; case Operators.OR: - writer.addCode("LeekValueManager.getLeekBooleanValue("); - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(".getBoolean() || "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(".getBoolean())"); + writer.addCode("("); + writer.addCode("ops("); + writer.getBoolean(mainblock, mExpression1); + writer.addCode(", " + mExpression1.operations + ") || ops("); + writer.getBoolean(mainblock, mExpression2); + writer.addCode(", " + mExpression2.operations + "))"); return; // Les unaires préfixés (!) case Operators.NOT: + writer.addCode("!"); + writer.getBoolean(mainblock, mExpression2); + return; + case Operators.BITNOT: + writer.addCode("bnot("); mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(".not(mUAI)"); + writer.addCode(")"); return; case Operators.UNARY_MINUS: - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(".opposite(mUAI)"); + if (mExpression2.getType().isNumber()) { + writer.addCode("-"); + mExpression2.writeJavaCode(mainblock, writer); + } else { + writer.addCode("minus("); + mExpression2.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + return; + case Operators.NEW: + if (mExpression2 instanceof LeekVariable) { + if (mainblock.getWordCompiler().getVersion() >= 3 && ((LeekVariable) mExpression2).getString().equals("Array")) { + writer.addCode("new ArrayLeekValue()"); + } else { + writer.addCode("execute("); + mExpression2.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } else { + mExpression2.writeJavaCode(mainblock, writer); + } return; - // Les unaires suffixés (++, --), Il a été vérifié au préalable // qu'on avait bien une L-Value case Operators.INCREMENT: - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(".increment(mUAI)"); + mExpression2.compileIncrement(mainblock, writer); return; case Operators.DECREMENT: - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(".decrement(mUAI)"); + mExpression2.compileDecrement(mainblock, writer); return; case Operators.PRE_INCREMENT: - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(".pre_increment(mUAI)"); + mExpression2.compilePreIncrement(mainblock, writer); return; case Operators.PRE_DECREMENT: - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(".pre_decrement(mUAI)"); + mExpression2.compilePreDecrement(mainblock, writer); return; // Les assignations case Operators.ASSIGN: - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(".set(mUAI, "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + // Assign without clone for LS 1.1 or reference + if (mainblock.getWordCompiler().getVersion() >= 2) { + mExpression1.compileSet(mainblock, writer, mExpression2); + } else if (mExpression2 instanceof LeekExpression && ((LeekExpression) mExpression2).getOperator() == Operators.REFERENCE) { + mExpression1.compileSet(mainblock, writer, ((LeekExpression) mExpression2).mExpression2); + } else { + mExpression1.compileSetCopy(mainblock, writer, mExpression2); + } return; case Operators.ADDASSIGN: - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(".add(mUAI, "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + mExpression1.compileAddEq(mainblock, writer, mExpression2); return; case Operators.MINUSASSIGN: - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(".minus(mUAI, "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + mExpression1.compileSubEq(mainblock, writer, mExpression2); return; case Operators.MODULUSASSIGN: - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(".modulus(mUAI, "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + mExpression1.compileModEq(mainblock, writer, mExpression2); return; case Operators.DIVIDEASSIGN: - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(".divide(mUAI, "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + mExpression1.compileDivEq(mainblock, writer, mExpression2); return; case Operators.MULTIPLIEASSIGN: - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(".multiply(mUAI, "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + mExpression1.compileMulEq(mainblock, writer, mExpression2); return; case Operators.POWERASSIGN: - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(".power(mUAI, "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + mExpression1.compilePowEq(mainblock, writer, mExpression2); return; case Operators.BITXOR_ASSIGN: - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(".bxor(mUAI, "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + mExpression1.compileBitXorEq(mainblock, writer, mExpression2); return; case Operators.BITAND_ASSIGN: - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(".band(mUAI, "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + mExpression1.compileBitAndEq(mainblock, writer, mExpression2); return; case Operators.BITOR_ASSIGN: - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(".bor(mUAI, "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + mExpression1.compileBitOrEq(mainblock, writer, mExpression2); return; case Operators.SHIFT_LEFT_ASSIGN: - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(".bleft(mUAI, "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + mExpression1.compileShiftLeftEq(mainblock, writer, mExpression2); return; case Operators.SHIFT_RIGHT_ASSIGN: - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(".bright(mUAI, "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + mExpression1.compileShiftRightEq(mainblock, writer, mExpression2); return; - case Operators.ROTATE_RIGHT_ASSIGN: - mExpression1.writeJavaCode(mainblock, writer); - writer.addCode(".brotate(mUAI, "); - mExpression2.writeJavaCode(mainblock, writer); - writer.addCode(")"); + case Operators.SHIFT_UNSIGNED_RIGHT_ASSIGN: + mExpression1.compileShiftUnsignedRightEq(mainblock, writer, mExpression2); return; case Operators.REFERENCE: - writer.addCode("new ReferenceLeekValue(mUAI, "); + // writer.addCode("new ReferenceLeekValue(" + writer.getAIThis() + ", "); + mExpression2.writeJavaCode(mainblock, writer); + // writer.addCode(")"); + return; + case Operators.INSTANCEOF: + writer.addCode("instanceOf("); + mExpression1.writeJavaCode(mainblock, writer); + writer.addCode(", "); mExpression2.writeJavaCode(mainblock, writer); writer.addCode(")"); return; } return; } + + @Override + public boolean isLeftValue() { + // return mOperator == Operators.DOT; + return false; + } + + @Override + public void analyze(WordCompiler compiler) { + + if (mOperator == Operators.REFERENCE && compiler.getVersion() >= 2) { + compiler.addError(new AnalyzeError(mOperatorToken, AnalyzeErrorLevel.WARNING, Error.REFERENCE_DEPRECATED)); + } + + if (mExpression1 != null) mExpression1.analyze(compiler); + if (mExpression2 != null) mExpression2.analyze(compiler); + + // Si on a affaire à une assignation, incrémentation ou autre du genre + // on doit vérifier qu'on a bien une variable (l-value) + if (mOperator == Operators.ADDASSIGN || mOperator == Operators.MINUSASSIGN || mOperator == Operators.DIVIDEASSIGN || mOperator == Operators.ASSIGN || mOperator == Operators.MODULUSASSIGN || mOperator == Operators.MULTIPLIEASSIGN || mOperator == Operators.POWERASSIGN || mOperator == Operators.BITOR_ASSIGN || mOperator == Operators.BITAND_ASSIGN || mOperator == Operators.BITXOR_ASSIGN || mOperator == Operators.SHIFT_LEFT_ASSIGN || mOperator == Operators.SHIFT_RIGHT_ASSIGN || mOperator == Operators.SHIFT_UNSIGNED_RIGHT_ASSIGN) { + if (!mExpression1.isLeftValue()) + compiler.addError(new AnalyzeError(mOperatorToken, AnalyzeErrorLevel.ERROR, Error.CANT_ASSIGN_VALUE)); + // throw new LeekExpressionException(mExpression1, LeekCompilerException.CANT_ASSIGN_VALUE); + if (mExpression1 instanceof LeekVariable) { + var v = (LeekVariable) mExpression1; + if (v.getVariableType() == VariableType.SYSTEM_FUNCTION || v.getVariableType() == VariableType.FUNCTION) { + compiler.getMainBlock().addRedefinedFunction(((LeekVariable) mExpression1).getName()); + } + } + if (mExpression1 instanceof LeekTabularValue) + ((LeekTabularValue) mExpression1).setLeftValue(true); + } + + if (mOperator == Operators.INCREMENT || mOperator == Operators.DECREMENT || mOperator == Operators.PRE_INCREMENT || mOperator == Operators.PRE_DECREMENT) { + if (!mExpression2.isLeftValue()) + compiler.addError(new AnalyzeError(mOperatorToken, AnalyzeErrorLevel.ERROR, Error.CANT_ASSIGN_VALUE)); + // throw new LeekExpressionException(mExpression2, LeekCompilerException.CANT_ASSIGN_VALUE); + if (mExpression2 instanceof LeekVariable) { + var v = (LeekVariable) mExpression2; + if (v.getVariableType() == VariableType.SYSTEM_FUNCTION || v.getVariableType() == VariableType.FUNCTION) { + compiler.getMainBlock().addRedefinedFunction(((LeekVariable) mExpression2).getName()); + } + } + if (mExpression2 instanceof LeekTabularValue) + ((LeekTabularValue) mExpression2).setLeftValue(true); + } + + if (mOperator == Operators.NOT || mOperator == Operators.EQUALS_EQUALS || mOperator == Operators.LESS || mOperator == Operators.MORE || mOperator == Operators.MOREEQUALS || mOperator == Operators.LESSEQUALS || mOperator == Operators.EQUALS || mOperator == Operators.AND || mOperator == Operators.OR || mOperator == Operators.NOTEQUALS || mOperator == Operators.NOT_EQUALS_EQUALS || mOperator == Operators.INSTANCEOF) { + type = Type.BOOL; + } + if (mOperator == Operators.BITAND || mOperator == Operators.BITNOT || mOperator == Operators.BITOR || mOperator == Operators.BITXOR || mOperator == Operators.SHIFT_LEFT || mOperator == Operators.SHIFT_RIGHT || mOperator == Operators.SHIFT_UNSIGNED_RIGHT) { + type = Type.INT; + } + if (mOperator == Operators.ADD && ((mExpression1.getType() == Type.STRING || mExpression2.getType() == Type.STRING))) { + type = Type.STRING; + } + if (mOperator == Operators.ADD || mOperator == Operators.MINUS) { + if (mExpression1.getType() == Type.INT) { + if (mExpression2.getType() == Type.INT) type = Type.INT; + if (mExpression2.getType() == Type.REAL) type = Type.REAL; + } + else if (mExpression1.getType() == Type.REAL) { + if (mExpression2.getType() == Type.INT || mExpression2.getType() == Type.REAL) type = Type.REAL; + } + } + if (mOperator == Operators.MULTIPLIE) { + if (mExpression1.getType().isNumber() && mExpression2.getType().isNumber()) { + if (mExpression1.getType() == Type.INT && mExpression2.getType() == Type.INT) { + type = Type.INT; + } else { + type = Type.REAL; + } + } + } + // if (mOperator == Operators.DIVIDE) { + // type = Type.REAL; + // } + if (mOperator == Operators.UNARY_MINUS) { + type = mExpression2.getType(); + } + // if (mOperator == Operators.POWER) { + // type = Type.REAL; + // } + + // Opérations + operations = (mExpression1 != null ? mExpression1.getOperations() : 0) + (mExpression2 != null ? mExpression2.getOperations() : 0); + if (mOperator == Operators.POWER) { + operations += LeekValue.POW_COST; + } else if (mOperator == Operators.POWERASSIGN) { + operations += LeekValue.POW_COST; + } else if (mOperator == Operators.MULTIPLIE) { + operations += LeekValue.MUL_COST; + } else if (mOperator == Operators.MULTIPLIEASSIGN) { + operations += LeekValue.MUL_COST; //+ 1; + } else if (mOperator == Operators.DIVIDE || mOperator == Operators.DIVIDEASSIGN) { + operations += LeekValue.DIV_COST; + } else if (mOperator == Operators.MODULUS || mOperator == Operators.MODULUSASSIGN) { + operations += LeekValue.MOD_COST; + } else if (mOperator == Operators.REFERENCE || mOperator == Operators.NEW) { + // 0 + } else if (mOperator == Operators.AND || mOperator == Operators.OR) { + operations = 0; + } else { + operations += 1; + } + } + + public boolean needsWrapper() { + return mOperator == Operators.OR || mOperator == Operators.AND || mOperator == Operators.ADD || mOperator == Operators.MINUS || mOperator == Operators.MULTIPLIE || mOperator == Operators.DIVIDE || mOperator == Operators.MODULUS || mOperator == Operators.POWER || mOperator == Operators.SHIFT_LEFT || mOperator == Operators.SHIFT_RIGHT || mOperator == Operators.BITAND || mOperator == Operators.BITOR || mOperator == Operators.BITXOR || mOperator == Operators.LESS || mOperator == Operators.MORE || mOperator == Operators.LESSEQUALS || mOperator == Operators.MOREEQUALS || mOperator == Operators.EQUALS || mOperator == Operators.EQUALS_EQUALS || mOperator == Operators.NOTEQUALS || mOperator == Operators.NOT_EQUALS_EQUALS; + } } diff --git a/src/main/java/leekscript/compiler/expression/LeekExpressionException.java b/src/main/java/leekscript/compiler/expression/LeekExpressionException.java index e07a68fb..1cbd02f0 100644 --- a/src/main/java/leekscript/compiler/expression/LeekExpressionException.java +++ b/src/main/java/leekscript/compiler/expression/LeekExpressionException.java @@ -1,25 +1,31 @@ package leekscript.compiler.expression; +import leekscript.common.Error; + public class LeekExpressionException extends Exception { /** - * + * */ private static final long serialVersionUID = 5724420043991763088L; private final AbstractExpression mExpression; - private final String mMessage; + private final Error mError; - public LeekExpressionException(AbstractExpression exp, String message) { + public LeekExpressionException(AbstractExpression exp, Error error) { mExpression = exp; - mMessage = message; + mError = error; } @Override public String getMessage() { - return mMessage; + return mError.name(); } public String getExpression() { return mExpression.getString(); } + + public Error getError() { + return mError; + } } diff --git a/src/main/java/leekscript/compiler/expression/LeekExpressionFunction.java b/src/main/java/leekscript/compiler/expression/LeekExpressionFunction.java index 66a4c4aa..06e7eae6 100644 --- a/src/main/java/leekscript/compiler/expression/LeekExpressionFunction.java +++ b/src/main/java/leekscript/compiler/expression/LeekExpressionFunction.java @@ -1,16 +1,34 @@ package leekscript.compiler.expression; import java.util.ArrayList; +import java.util.HashMap; +import leekscript.compiler.AnalyzeError; +import leekscript.compiler.IAWord; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; +import leekscript.compiler.AnalyzeError.AnalyzeErrorLevel; +import leekscript.compiler.bloc.ClassMethodBlock; +import leekscript.compiler.bloc.FunctionBlock; import leekscript.compiler.bloc.MainLeekBlock; +import leekscript.compiler.expression.LeekVariable.VariableType; +import leekscript.compiler.instruction.ClassDeclarationInstruction.ClassDeclarationMethod; +import leekscript.runner.CallableVersion; +import leekscript.runner.ILeekFunction; +import leekscript.runner.LeekFunctions; +import leekscript.common.AccessLevel; +import leekscript.common.Error; +import leekscript.common.Type; public class LeekExpressionFunction extends AbstractExpression { + private IAWord openParenthesis = null; private final ArrayList mParameters = new ArrayList(); private AbstractExpression mExpression = null; + private Type type = Type.ANY; - public LeekExpressionFunction() { + public LeekExpressionFunction(IAWord openParenthesis) { + this.openParenthesis = openParenthesis; } public void setExpression(AbstractExpression expression) { @@ -22,10 +40,15 @@ public void addParameter(AbstractExpression param) { } @Override - public int getType() { + public int getNature() { return FUNCTION; } + @Override + public Type getType() { + return type; + } + @Override public String getString() { String str = mExpression.getString() + "("; @@ -37,25 +60,376 @@ public String getString() { } @Override - public boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionException { - if(mExpression == null || !mExpression.validExpression(mainblock)) return false; + public boolean validExpression(WordCompiler compiler, MainLeekBlock mainblock) throws LeekExpressionException { + if(mExpression == null || !mExpression.validExpression(compiler, mainblock)) return false; //Vérification de chaque paramètre for(AbstractExpression parameter : mParameters){ - parameter.validExpression(mainblock); + parameter.validExpression(compiler, mainblock); } return true; } @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - mExpression.writeJavaCode(mainblock, writer); - writer.addCode(".executeFunction(mUAI, new AbstractLeekValue[]{"); - for(int i = 0; i < mParameters.size(); i++){ - if(i > 0) writer.addCode(", "); - if(i < mParameters.size()) mParameters.get(i).writeJavaCode(mainblock, writer); - else writer.addCode("LeekValueManager.NULL"); + writer.addCode("load("); + compileL(mainblock, writer); + writer.addCode(")"); + } + + @Override + public void compileL(MainLeekBlock mainblock, JavaWriter writer) { + boolean addComma = true; + boolean addBrace = false; + boolean addParen = false; + FunctionBlock user_function = null; + ILeekFunction system_function = null; + + if (mExpression instanceof LeekObjectAccess) { + // Object access : object.field() + var object = ((LeekObjectAccess) mExpression).getObject(); + var field = ((LeekObjectAccess) mExpression).getField(); + + if (object instanceof LeekVariable && ((LeekVariable) object).getVariableType() == VariableType.SUPER) { + // super.field() + var from_class = writer.currentBlock instanceof ClassMethodBlock ? "u_class" : "null"; + writer.addCode("u_this.callSuperMethod(this, \"" + field + "_" + mParameters.size() + "\", " + from_class); + } else if (object instanceof LeekVariable && ((LeekVariable) object).getVariableType() == VariableType.CLASS) { + // Class.method() : Méthode statique connue + var v = (LeekVariable) object; + String methodName = "u_" + v.getClassDeclaration().getStaticMethodName(field, mParameters.size()); + writer.addCode("profileStatic(\"" + methodName + "\", getOperations(), "); + writer.addCode(methodName + "("); + addComma = false; + addParen = true; + } else if (object instanceof LeekVariable && ((LeekVariable) object).getVariableType() == VariableType.THIS) { + // this.method() : Méthode connue + writer.addCode("callObjectAccess(u_this, \"" + field + "\", \"" + field + "_" + mParameters.size() + "\", u_class"); + } else { + // object.field() : Méthode ou bien appel d'un champ + writer.addCode("callObjectAccess("); + object.writeJavaCode(mainblock, writer); + var from_class = writer.currentBlock instanceof ClassMethodBlock ? "u_class" : "null"; + writer.addCode(", \"" + field + "\", \"" + field + "_" + mParameters.size() + "\", " + from_class); + } + } else if (mExpression instanceof LeekTabularValue) { + var object = ((LeekTabularValue) mExpression).getTabular(); + if (object instanceof LeekVariable && ((LeekVariable) object).getVariableType() == VariableType.SUPER) { + writer.addCode("u_this.callSuperMethod(mUAI, u_class, "); + ((LeekTabularValue) mExpression).getCase().writeJavaCode(mainblock, writer); + writer.addCode(".getString(mUAI) + \"_" + mParameters.size() + "\", "); + var from_class = writer.currentBlock instanceof ClassMethodBlock ? "u_class" : "null"; + writer.addCode(from_class); + } else { + writer.addCode("LeekValueManager.executeArrayAccess(" + writer.getAIThis() + ", "); + object.writeJavaCode(mainblock, writer); + writer.addCode(", "); + ((LeekTabularValue) mExpression).getCase().writeJavaCode(mainblock, writer); + writer.addCode(", "); + writer.addCode(writer.currentBlock instanceof ClassMethodBlock ? "u_class" : "null"); + } + } else if (mExpression instanceof LeekVariable && ((LeekVariable) mExpression).getVariableType() == VariableType.SUPER) { + // Super constructor + var variable = (LeekVariable) mExpression; + writer.addCode("u_" + variable.getClassDeclaration().getParent().getName()); + writer.addCode(".callConstructor(u_this"); + } else if (mExpression instanceof LeekVariable && ((LeekVariable) mExpression).getVariableType() == VariableType.METHOD) { + // Méthode connue + // String methodName = "u_" + mainblock.getWordCompiler().getCurrentClass().getMethodName(((LeekVariable) mExpression).getName(), mParameters.size()); + // writer.addCode(methodName + "(u_this"); + writer.addCode("callMethod(u_this, \"" + ((LeekVariable) mExpression).getName() + "_" + mParameters.size() + "\", u_class"); + + } else if (mExpression instanceof LeekVariable && ((LeekVariable) mExpression).getVariableType() == VariableType.STATIC_METHOD) { + // Méthode statique connue + String methodName = "u_" + mainblock.getWordCompiler().getCurrentClass().getStaticMethodName(((LeekVariable) mExpression).getName(), mParameters.size()); + writer.addCode(methodName + "("); + addComma = false; + } else if (mExpression instanceof LeekVariable && mainblock.isRedefinedFunction(((LeekVariable) mExpression).getName())) { + writer.addCode("rfunction_" + ((LeekVariable) mExpression).getName()); + writer.addCode(".execute("); + addComma = false; + } else if (mExpression instanceof LeekVariable && ((LeekVariable) mExpression).getVariableType() == VariableType.SYSTEM_FUNCTION) { + var variable = (LeekVariable) mExpression; + system_function = LeekFunctions.getValue(variable.getName()); + + if (system_function.getVersions() != null) { + // var version = checkArgumentsStatically(system_function); + // System.out.println("version = " + version); + // if (version != null) { + // var signature = buildTypesSignature(version.arguments); + // writer.addCode("" + system_function + "_" + signature + "("); + // } else { + writer.addCode("" + system_function + "("); + // } + addComma = false; + } else { + // writer.addCode("" + system_function + "_("); + String namespace = LeekFunctions.getNamespace(variable.getName()); + writer.addCode("sysexec(" + namespace + "." + variable.getName()); + } + } else if (mExpression instanceof LeekVariable && ((LeekVariable) mExpression).getVariableType() == VariableType.FUNCTION) { + writer.addCode("f_"); + writer.addCode(((LeekVariable) mExpression).getName()); + writer.addCode("("); + addComma = false; + user_function = mainblock.getUserFunction(((LeekVariable) mExpression).getName()); + } else { + if (mExpression.isLeftValue() && !mExpression.nullable()) { + writer.addCode("execute("); + mExpression.writeJavaCode(mainblock, writer); + // addComma = false; + } else { + writer.addCode("execute("); + mExpression.writeJavaCode(mainblock, writer); + } } - writer.addCode("})"); + + int argCount = mParameters.size(); + if (system_function != null) argCount = Math.max(argCount, system_function.getArguments()); + for (int i = 0; i < argCount; i++) { + if (i > 0 || addComma) writer.addCode(", "); + if (i < mParameters.size()) { + var parameter = mParameters.get(i); + // Java doesn't like a single null for Object... argument + if (argCount == 1 && parameter.getType() == Type.NULL && user_function == null) { + writer.addCode("new Object[] { null }"); + continue; + } + if (mainblock.getCompiler().getCurrentAI().getVersion() >= 2) { + parameter.writeJavaCode(mainblock, writer); + } else { + if (user_function != null) { + // if (user_function.isReference(i)) { + parameter.compileL(mainblock, writer); + // } else { + // writer.compileClone(mainblock, parameter); + // } + } else if (system_function != null) { + writer.compileLoad(mainblock, parameter); + } else { + parameter.compileL(mainblock, writer); + } + } + } else { + // Java doesn't like a single null for Object... argument + if (argCount == 1) { + writer.addCode("new Object[] { null }"); + } else { + writer.addCode("null"); + } + } + } + if (addBrace) { + writer.addCode("}"); + } + if (addParen) { + writer.addCode(")"); + } + writer.addCode(")"); + writer.addPosition(openParenthesis); + } + + @Override + public void analyze(WordCompiler compiler) { + operations = 0; + mExpression.analyze(compiler); + operations += mExpression.getOperations(); + for (AbstractExpression parameter : mParameters) { + parameter.analyze(compiler); + operations += parameter.getOperations(); + } + + if (mExpression instanceof LeekVariable) { + var v = (LeekVariable) mExpression; + + if (v.getVariableType() == VariableType.METHOD) { // La variable est analysée comme une méthode, mais ça peut être une fonction système, + + // on regarde si le nombre d'arguments est correct + var current = compiler.getCurrentClass(); + while (current != null) { + var methods = current.getMethod(v.getName()); + if (methods != null) { + for (var count : methods.keySet()) { + if (count == mParameters.size()) { + return; // OK + } + } + } + current = current.getParent(); + } + // Est-ce que c'est une fonction système ? + var f = LeekFunctions.getValue(v.getName()); + if (f != null) { + if (mParameters.size() >= f.getArgumentsMin() && mParameters.size() <= f.getArguments()) { + v.setVariableType(VariableType.SYSTEM_FUNCTION); + return; // OK, fonction système + } + } + // Sinon, erreur de méthode + compiler.addError(new AnalyzeError(v.getToken(), AnalyzeErrorLevel.ERROR, Error.INVALID_PARAMETER_COUNT)); + + } else if (v.getVariableType() == VariableType.STATIC_METHOD) { + + // on regarde si le nombre d'arguments est correct + var current = compiler.getCurrentClass(); + while (current != null) { + var methods = current.getStaticMethod(v.getName()); + if (methods != null) { + for (var count : methods.keySet()) { + if (count == mParameters.size()) { + return; // OK + } + } + } + current = current.getParent(); + } + // Est-ce que c'est une fonction système ? + var f = LeekFunctions.getValue(v.getName()); + if (f != null) { + if (mParameters.size() >= f.getArgumentsMin() && mParameters.size() <= f.getArguments()) { + v.setVariableType(VariableType.SYSTEM_FUNCTION); + return; // OK, fonction système + } + } + // Sinon, erreur de méthode + compiler.addError(new AnalyzeError(v.getToken(), AnalyzeErrorLevel.ERROR, Error.INVALID_PARAMETER_COUNT)); + + } else if (v.getVariableType() == VariableType.FUNCTION) { + int nb_params = LeekFunctions.isFunction(v.getName()); + if (nb_params == -1) { + nb_params = compiler.getMainBlock().getUserFunctionParametersCount(v.getName()); + if (mParameters.size() != nb_params) { + compiler.addError(new AnalyzeError(v.getToken(), AnalyzeErrorLevel.ERROR, Error.INVALID_PARAMETER_COUNT)); + } + } else { + var f = LeekFunctions.getValue(v.getName()); + if (mParameters.size() > nb_params || mParameters.size() < f.getArgumentsMin()) { + compiler.addError(new AnalyzeError(v.getToken(), AnalyzeErrorLevel.ERROR, Error.INVALID_PARAMETER_COUNT)); + } + var version = checkArgumentsStatically(f); + if (version != null) { + type = version.return_type; + } + } + } else if (v.getVariableType() == VariableType.SYSTEM_FUNCTION) { + + if (compiler.getVersion() >= 3) { + var f = LeekFunctions.getValue(v.getName()); + if (mParameters.size() > f.getArguments() || mParameters.size() < f.getArgumentsMin()) { + compiler.addError(new AnalyzeError(v.getToken(), AnalyzeErrorLevel.ERROR, Error.INVALID_PARAMETER_COUNT)); + } + } + var system_function = LeekFunctions.getValue(v.getName()); + if (system_function.getReturnType() != null) { + type = system_function.getReturnType(); + } + } else if (v.getVariableType() == VariableType.CLASS) { + + var clazz = v.getClassDeclaration(); + var constructor = clazz.getConstructor(mParameters.size()); + if (constructor == null) { + compiler.addError(new AnalyzeError(v.getToken(), AnalyzeErrorLevel.ERROR, Error.UNKNOWN_CONSTRUCTOR, new String[] { clazz.getName() })); + } else if (constructor.level == AccessLevel.PRIVATE && compiler.getCurrentClass() != clazz) { + compiler.addError(new AnalyzeError(v.getToken(), AnalyzeErrorLevel.ERROR, Error.PRIVATE_CONSTRUCTOR, new String[] { clazz.getName() })); + } else if (constructor.level == AccessLevel.PROTECTED && (compiler.getCurrentClass() == null || !compiler.getCurrentClass().descendsFrom(clazz))) { + compiler.addError(new AnalyzeError(v.getToken(), AnalyzeErrorLevel.ERROR, Error.PROTECTED_CONSTRUCTOR, new String[] { clazz.getName() })); + } + + } else if (v.getVariableType() == VariableType.SUPER) { + + var clazz = v.getClassDeclaration().getParent(); + if (clazz != null) { + var constructor = clazz.getConstructor(mParameters.size()); + operations += 1; + if (constructor == null) { + compiler.addError(new AnalyzeError(v.getToken(), AnalyzeErrorLevel.ERROR, Error.UNKNOWN_CONSTRUCTOR, new String[] { clazz.getName() })); + } else if (constructor.level == AccessLevel.PRIVATE && compiler.getCurrentClass() != clazz) { + compiler.addError(new AnalyzeError(v.getToken(), AnalyzeErrorLevel.ERROR, Error.PRIVATE_CONSTRUCTOR, new String[] { clazz.getName() })); + } else if (constructor.level == AccessLevel.PROTECTED && (compiler.getCurrentClass() == null || !compiler.getCurrentClass().descendsFrom(clazz))) { + compiler.addError(new AnalyzeError(v.getToken(), AnalyzeErrorLevel.ERROR, Error.PROTECTED_CONSTRUCTOR, new String[] { clazz.getName() })); + } + } + } + } else if (mExpression instanceof LeekObjectAccess) { + + var oa = (LeekObjectAccess) mExpression; + if (oa.getObject() instanceof LeekVariable) { + var v = (LeekVariable) oa.getObject(); + + if (v.getVariableType() == VariableType.THIS) { + + // on regarde si le nombre d'arguments est correct + var current = compiler.getCurrentClass(); + while (current != null) { + var methods = current.getMethod(oa.getField()); + if (methods != null) { + for (var count : methods.keySet()) { + if (count == mParameters.size()) { + return; // OK + } + } + } + current = current.getParent(); + } + // Si la classe a un field du même nom, pas d'erreur + if (!compiler.getCurrentClass().hasField(oa.getField())) { + compiler.addError(new AnalyzeError(oa.getFieldToken(), AnalyzeErrorLevel.ERROR, Error.INVALID_PARAMETER_COUNT)); + } + + } else if (v.getVariableType() == VariableType.CLASS || v.getVariableType() == VariableType.THIS_CLASS) { + + var clazz = v.getVariableType() == VariableType.CLASS ? v.getClassDeclaration() : compiler.getCurrentClass(); + operations += 1; + // on regarde si le nombre d'arguments est correct + var current = clazz; + HashMap methods = null; + while (current != null) { + methods = current.getStaticMethod(oa.getField()); + if (methods != null) { + for (var count : methods.keySet()) { + if (count == mParameters.size()) { + var staticMethod = methods.get(count); + + if (staticMethod.level == AccessLevel.PRIVATE && compiler.getCurrentClass() != clazz) { + compiler.addError(new AnalyzeError(oa.getFieldToken(), AnalyzeErrorLevel.ERROR, Error.PRIVATE_STATIC_METHOD, new String[] { clazz.getName(), oa.getField() })); + } else if (staticMethod.level == AccessLevel.PROTECTED && (compiler.getCurrentClass() == null || !compiler.getCurrentClass().descendsFrom(clazz))) { + compiler.addError(new AnalyzeError(oa.getFieldToken(), AnalyzeErrorLevel.ERROR, Error.PROTECTED_STATIC_METHOD, new String[] { clazz.getName(), oa.getField() })); + } + return; // OK + } + } + } + current = current.getParent(); + } + + if (methods != null) { // Trouvée mais mauvais nombre d'arguments + compiler.addError(new AnalyzeError(oa.getFieldToken(), AnalyzeErrorLevel.ERROR, Error.INVALID_PARAMETER_COUNT, new String[] { clazz.getName(), oa.getField() })); + } else { // Pas trouvée + compiler.addError(new AnalyzeError(oa.getFieldToken(), AnalyzeErrorLevel.ERROR, Error.UNKNOWN_STATIC_METHOD, new String[] { clazz.getName(), oa.getField() })); + } + } + } + } + } + + CallableVersion checkArgumentsStatically(ILeekFunction function) { + var versions = function.getVersions(); + if (versions == null) return null; + + for (var version : versions) { + if (checkArgumentsStatically(version)) { + return version; + } + } + return null; + } + + private boolean checkArgumentsStatically(CallableVersion version) { + for (int i = 0; i < version.arguments.length; ++i) { + if (!version.arguments[i].accepts(mParameters.get(i).getType())) { + return false; + } + } + return true; } } diff --git a/src/main/java/leekscript/compiler/expression/LeekFunction.java b/src/main/java/leekscript/compiler/expression/LeekFunction.java deleted file mode 100644 index 17a9dcf2..00000000 --- a/src/main/java/leekscript/compiler/expression/LeekFunction.java +++ /dev/null @@ -1,119 +0,0 @@ -package leekscript.compiler.expression; - -import java.util.ArrayList; - -import leekscript.compiler.JavaWriter; -import leekscript.compiler.bloc.FunctionBlock; -import leekscript.compiler.bloc.MainLeekBlock; -import leekscript.compiler.exceptions.LeekCompilerException; -import leekscript.runner.ILeekFunction; -import leekscript.runner.LeekFunctions; - -public class LeekFunction extends AbstractExpression { - - private final String mName; - private final ArrayList mParameters = new ArrayList(); - - public LeekFunction(String name) { - mName = name; - } - - public void addParameter(AbstractExpression param) { - mParameters.add(param); - } - - @Override - public int getType() { - return FUNCTION; - } - - @Override - public String getString() { - String str = mName + "("; - for (int i = 0; i < mParameters.size(); i++) { - if (i > 0) - str += ", "; - str += mParameters.get(i).getString(); - } - return str + ")"; - } - - @Override - public boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionException { - int nb_params = LeekFunctions.isFunction(mName); - if (nb_params == -1) { - nb_params = mainblock.getUserFunctionParametersCount(mName); - if (nb_params == -1) - throw new LeekExpressionException(this, LeekCompilerException.FUNCTION_NOT_EXISTS); - if (mParameters.size() != nb_params) - throw new LeekExpressionException(this, LeekCompilerException.INVALID_PAREMETER_COUNT); - } - else { - ILeekFunction f = LeekFunctions.getValue(mName); - if (mParameters.size() > nb_params || mParameters.size() < f.getArgumentsMin()) - throw new LeekExpressionException(this, LeekCompilerException.INVALID_PAREMETER_COUNT); - } - - // Vérification de chaque paramètre - for (AbstractExpression parameter : mParameters) { - parameter.validExpression(mainblock); - } - return true; - } - - @Override - public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - if (mainblock.isRedefinedFunction(mName)) { - writer.addCode("rfunction_" + mName); - writer.addCode(".executeFunction(mUAI, new AbstractLeekValue[]{"); - for (int i = 0; i < mParameters.size(); i++) { - if (i > 0) - writer.addCode(", "); - if (i < mParameters.size()) - mParameters.get(i).writeJavaCode(mainblock, writer); - else - writer.addCode("LeekValueManager.NULL"); - } - writer.addCode("})"); - } - else { - FunctionBlock user_function = mainblock.getUserFunction(mName); - if (user_function != null) { - writer.addCode("user_function_" + mName + "("); - - for (int i = 0; i < mParameters.size(); i++) { - if (i > 0) - writer.addCode(", "); - if (user_function.isReference(i)) - mParameters.get(i).writeJavaCode(mainblock, writer); - else { - writer.addCode("LeekOperations.clone(mUAI, "); - mParameters.get(i).writeJavaCode(mainblock, writer); - writer.addCode(".getValue())"); - } - } - writer.addCode(")"); - } - else { - ILeekFunction fun = LeekFunctions.getValue(mName); - int nb_params = fun.getArguments(); - if (!writer.hasDebug() && (mName.equals("debug") || mName.equalsIgnoreCase("mark") || mName.equals("pause"))) { - writer.addCode("nothing(LeekValueManager.NULL)"); - return; - } - writer.addCode("LeekFunctions.executeFunction(mUAI, " + fun.getNamespace() + "." + mName + ", new AbstractLeekValue[]{"); - for (int i = 0; i < nb_params; i++) { - if (i > 0) - writer.addCode(", "); - if (i < mParameters.size()) { - mParameters.get(i).writeJavaCode(mainblock, writer); - writer.addCode(".getValue()"); - } - else - writer.addCode("LeekValueManager.NULL"); - } - writer.addCode("}, " + mParameters.size() + ")"); - } - } - } -} diff --git a/src/main/java/leekscript/compiler/expression/LeekFunctionValue.java b/src/main/java/leekscript/compiler/expression/LeekFunctionValue.java deleted file mode 100644 index 2a816ddc..00000000 --- a/src/main/java/leekscript/compiler/expression/LeekFunctionValue.java +++ /dev/null @@ -1,54 +0,0 @@ -package leekscript.compiler.expression; - -import leekscript.compiler.JavaWriter; -import leekscript.compiler.bloc.FunctionBlock; -import leekscript.compiler.bloc.MainLeekBlock; - -public class LeekFunctionValue extends AbstractExpression { - - private final String mFunctionName; - private final String mFunctionNamespace; - - public LeekFunctionValue(String funcname, String namespace) { - mFunctionName = funcname; - mFunctionNamespace = namespace; - } - - @Override - public int getType() { - return 0; - } - - @Override - public String getString() { - return "Fonction[" + mFunctionName + "]"; - } - - @Override - public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - //return "Fonction[" + mFunctionName + "]"; - //FunctionLeekValue - if(mainblock.isRedefinedFunction(mFunctionName)){ - writer.addCode("rfunction_" + mFunctionName); - } - else{ - FunctionBlock user_function = mainblock.getUserFunction(mFunctionName); - if(user_function != null){ - writer.addCode("new FunctionLeekValue(" + user_function.getId() + ")"); - } - else{ - writer.addCode("LeekValueManager.getFunction(" + mFunctionNamespace + "." + mFunctionName + ")"); - } - } - } - - @Override - public boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionException { - return true; - } - - public String getFunctionName() { - return mFunctionName; - } - -} diff --git a/src/main/java/leekscript/compiler/expression/LeekGlobal.java b/src/main/java/leekscript/compiler/expression/LeekGlobal.java deleted file mode 100644 index dec196ca..00000000 --- a/src/main/java/leekscript/compiler/expression/LeekGlobal.java +++ /dev/null @@ -1,35 +0,0 @@ -package leekscript.compiler.expression; - -import leekscript.compiler.JavaWriter; -import leekscript.compiler.bloc.MainLeekBlock; - -public class LeekGlobal extends AbstractExpression { - - private final String mVariable; - - public LeekGlobal(String variable) { - mVariable = variable; - } - - @Override - public int getType() { - return GLOBAL; - } - - @Override - public String getString() { - return mVariable; - } - - @Override - public boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionException { - //Pour une globale, la vérification est faite avant l'ajout donc pas besoin de refaire - return true; - } - - @Override - public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - writer.addCode("(globale_" + mVariable + "==null?LeekValueManager.NULL:globale_" + mVariable + ")"); - } - -} diff --git a/src/main/java/leekscript/compiler/expression/LeekNull.java b/src/main/java/leekscript/compiler/expression/LeekNull.java index 1444915a..961d84d6 100644 --- a/src/main/java/leekscript/compiler/expression/LeekNull.java +++ b/src/main/java/leekscript/compiler/expression/LeekNull.java @@ -1,29 +1,40 @@ package leekscript.compiler.expression; +import leekscript.common.Type; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.bloc.MainLeekBlock; public class LeekNull extends AbstractExpression { @Override - public int getType() { + public int getNature() { return NULL; } + @Override + public Type getType() { + return Type.NULL; + } + @Override public String getString() { return "null"; } @Override - public boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionException { + public boolean validExpression(WordCompiler compiler, MainLeekBlock mainblock) throws LeekExpressionException { //Pour une valeur null pas de soucis return true; } @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - writer.addCode("LeekValueManager.NULL"); + writer.addCode("null"); } + @Override + public void analyze(WordCompiler compiler) { + + } } diff --git a/src/main/java/leekscript/compiler/expression/LeekNumber.java b/src/main/java/leekscript/compiler/expression/LeekNumber.java index bd150249..69776842 100644 --- a/src/main/java/leekscript/compiler/expression/LeekNumber.java +++ b/src/main/java/leekscript/compiler/expression/LeekNumber.java @@ -1,40 +1,63 @@ package leekscript.compiler.expression; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Locale; + +import leekscript.common.Type; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.bloc.MainLeekBlock; public class LeekNumber extends AbstractExpression { private final double mValue; + private Type type; - public LeekNumber(double value) { + public LeekNumber(double value, Type type) { mValue = value; + this.type = type; } @Override - public int getType() { + public int getNature() { return NUMBER; } + @Override + public Type getType() { + return type; + } + @Override public String getString() { - return String.valueOf(mValue); + if (type == Type.REAL) { + var formatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US); + formatter.setMaximumFractionDigits(15); + formatter.setGroupingUsed(false); + return formatter.format(mValue); + } else { + return String.valueOf((int) mValue); + } } @Override - public boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionException { - //Pour un nombre pas de soucis + public boolean validExpression(WordCompiler compiler, MainLeekBlock mainblock) throws LeekExpressionException { + // Pour un nombre pas de soucis return true; } @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - if(mValue == (int) mValue){ - writer.addCode("LeekValueManager.getLeekIntValue(" + ((int) mValue) + ")"); - } - else{ - writer.addCode("new DoubleLeekValue(" + mValue + ")"); + if (type == Type.INT) { + writer.addCode(String.valueOf((int) mValue)); + } else { + writer.addCode(String.valueOf(mValue)); } } + @Override + public void analyze(WordCompiler compiler) { + + } } diff --git a/src/main/java/leekscript/compiler/expression/LeekObject.java b/src/main/java/leekscript/compiler/expression/LeekObject.java new file mode 100644 index 00000000..fe9ecb14 --- /dev/null +++ b/src/main/java/leekscript/compiler/expression/LeekObject.java @@ -0,0 +1,73 @@ +package leekscript.compiler.expression; + +import java.util.LinkedHashMap; + +import leekscript.common.Type; +import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; +import leekscript.compiler.bloc.MainLeekBlock; + +public class LeekObject extends AbstractExpression { + + private final LinkedHashMap mValues = new LinkedHashMap<>(); + + public void addEntry(String key, AbstractExpression value) { + mValues.put(key, value); + } + + @Override + public int getNature() { + return OBJECT; + } + + @Override + public Type getType() { + return Type.OBJECT; + } + + @Override + public String getString() { + String str = "{"; + int i = 0; + for (var entry : mValues.entrySet()) { + if (i++ > 0) str += ", "; + str += entry.getKey() + ": "; + str += entry.getValue().getString(); + } + return str + "}"; + } + + @Override + public boolean validExpression(WordCompiler compiler, MainLeekBlock mainblock) throws LeekExpressionException { + for (var entry : mValues.entrySet()) { + entry.getValue().validExpression(compiler, mainblock); + } + return true; + } + + @Override + public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { + writer.addCode("new ObjectLeekValue(" + writer.getAIThis() + ", new String[] { "); + int i = 0; + for (var entry : mValues.entrySet()) { + if (i++ != 0) writer.addCode(", "); + writer.addCode("\"" + entry.getKey() + "\""); + } + writer.addCode(" }, new Object[] { "); + i = 0; + for (var entry : mValues.entrySet()) { + if (i++ != 0) writer.addCode(", "); + entry.getValue().writeJavaCode(mainblock, writer); + } + writer.addCode(" })"); + } + + @Override + public void analyze(WordCompiler compiler) { + operations = 0; + for (var value : mValues.entrySet()) { + value.getValue().analyze(compiler); + operations += value.getValue().getOperations(); + } + } +} diff --git a/src/main/java/leekscript/compiler/expression/LeekObjectAccess.java b/src/main/java/leekscript/compiler/expression/LeekObjectAccess.java new file mode 100644 index 00000000..f8885fb6 --- /dev/null +++ b/src/main/java/leekscript/compiler/expression/LeekObjectAccess.java @@ -0,0 +1,322 @@ +package leekscript.compiler.expression; + +import leekscript.compiler.AnalyzeError; +import leekscript.compiler.IAWord; +import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; +import leekscript.compiler.AnalyzeError.AnalyzeErrorLevel; +import leekscript.compiler.bloc.ClassMethodBlock; +import leekscript.compiler.bloc.MainLeekBlock; +import leekscript.compiler.expression.LeekVariable.VariableType; +import leekscript.common.Error; +import leekscript.common.Type; + +public class LeekObjectAccess extends AbstractExpression { + + private AbstractExpression object; + private IAWord field; + + public LeekObjectAccess(AbstractExpression object, IAWord field) { + this.object = object; + this.field = field; + } + + @Override + public int getNature() { + return OBJECT_ACCESS; + } + + @Override + public Type getType() { + return Type.ANY; + } + + @Override + public String getString() { + return object.getString() + "." + field.getWord(); + } + + public AbstractExpression getObject() { + return object; + } + + @Override + public boolean validExpression(WordCompiler compiler, MainLeekBlock mainblock) throws LeekExpressionException { + return object.validExpression(compiler, mainblock); + } + + @Override + public boolean isLeftValue() { + return true; + } + + @Override + public boolean nullable() { + return object.nullable(); + } + + @Override + public void analyze(WordCompiler compiler) { + // System.out.println("oa " + getString()); + object.analyze(compiler); + operations = 1 + object.operations; + + if (field.getWord().equals("class")) { + return; // .class is available everywhere + } + + if (object instanceof LeekVariable) { + var v = (LeekVariable) object; + if (v.getName().equals("this")) { + // this, check field exists in class + var clazz = compiler.getCurrentClass(); + if (clazz != null && !clazz.hasMember(field.getWord())) { + compiler.addError(new AnalyzeError(field, AnalyzeErrorLevel.ERROR, Error.CLASS_MEMBER_DOES_NOT_EXIST, new String[] { clazz.getName(), field.getWord() })); + } + } else if (v.getVariableType() == VariableType.CLASS || v.getVariableType() == VariableType.THIS_CLASS) { + var clazz = v.getVariableType() == VariableType.CLASS ? v.getClassDeclaration() : compiler.getCurrentClass(); + operations -= 1; + if (field.getWord().equals("name") || field.getWord().equals("super") || field.getWord().equals("fields") || field.getWord().equals("staticFields") || field.getWord().equals("methods") || field.getWord().equals("staticMethods")) { + return; // OK + } + if (clazz.hasStaticMember(field.getWord())) { + return; // OK + } + if (clazz.hasMethod(field.getWord())) { + return; // OK + } + compiler.addError(new AnalyzeError(field, AnalyzeErrorLevel.ERROR, Error.CLASS_STATIC_MEMBER_DOES_NOT_EXIST, new String[] { clazz.getName(), field.getWord() })); + } + } + } + + public String getField() { + return field.getWord(); + } + + public IAWord getFieldToken() { + return field; + } + + @Override + public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { + if (mainblock.getWordCompiler().getVersion() >= 3 && field.getWord().equals("class")) { + writer.addCode("getClass("); + object.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("getField("); + object.writeJavaCode(mainblock, writer); + var from_class = writer.currentBlock instanceof ClassMethodBlock ? "u_class" : "null"; + writer.addCode(", \"" + field.getWord() + "\", " + from_class + ")"); + } + } + + @Override + public void compileL(MainLeekBlock mainblock, JavaWriter writer) { + assert (object.isLeftValue() && !object.nullable()); + + if (mainblock.getWordCompiler().getVersion() >= 3 && field.getWord().equals("class")) { + writer.addCode("getClass("); + object.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("getField("); + object.writeJavaCode(mainblock, writer); + var from_class = writer.currentBlock instanceof ClassMethodBlock ? "u_class" : "null"; + writer.addCode(", \"" + field.getWord() + "\", " + from_class + ")"); + } + } + + @Override + public void compileSet(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert (object.isLeftValue() && !object.nullable()); + + writer.addCode("setField("); + object.writeJavaCode(mainblock, writer); + writer.addCode(", \"" + field.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + public void compileIncrement(MainLeekBlock mainblock, JavaWriter writer) { + assert (object.isLeftValue() && !object.nullable()); + + writer.addCode("field_inc("); + object.writeJavaCode(mainblock, writer); + writer.addCode(", \"" + field.getWord() + "\")"); + } + + @Override + public void compilePreIncrement(MainLeekBlock mainblock, JavaWriter writer) { + assert (object.isLeftValue() && !object.nullable()); + + writer.addCode("field_pre_inc("); + object.writeJavaCode(mainblock, writer); + writer.addCode(", \"" + field.getWord() + "\")"); + } + + @Override + public void compileDecrement(MainLeekBlock mainblock, JavaWriter writer) { + assert (object.isLeftValue() && !object.nullable()); + + writer.addCode("field_dec("); + object.writeJavaCode(mainblock, writer); + writer.addCode(", \"" + field.getWord() + "\")"); + } + + @Override + public void compilePreDecrement(MainLeekBlock mainblock, JavaWriter writer) { + assert (object.isLeftValue() && !object.nullable()); + + writer.addCode("field_pre_dec("); + object.writeJavaCode(mainblock, writer); + writer.addCode(", \"" + field.getWord() + "\")"); + } + + @Override + public void compileAddEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert (object.isLeftValue() && !object.nullable()); + + writer.addCode("field_add_eq("); + object.writeJavaCode(mainblock, writer); + writer.addCode(", \"" + field.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + + public void compileSubEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert (object.isLeftValue() && !object.nullable()); + + writer.addCode("field_sub_eq("); + object.writeJavaCode(mainblock, writer); + writer.addCode(", \"" + field.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + + public void compileMulEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert (object.isLeftValue() && !object.nullable()); + + writer.addCode("field_mul_eq("); + object.writeJavaCode(mainblock, writer); + writer.addCode(", \"" + field.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + + public void compilePowEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert (object.isLeftValue() && !object.nullable()); + + writer.addCode("field_pow_eq("); + object.writeJavaCode(mainblock, writer); + writer.addCode(", \"" + field.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + + public void compileDivEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert (object.isLeftValue() && !object.nullable()); + + writer.addCode("field_div_eq("); + object.writeJavaCode(mainblock, writer); + writer.addCode(", \"" + field.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + + public void compileModEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert (object.isLeftValue() && !object.nullable()); + + writer.addCode("field_mod_eq("); + object.writeJavaCode(mainblock, writer); + writer.addCode(", \"" + field.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + + public void compileBitOrEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert (object.isLeftValue() && !object.nullable()); + + writer.addCode("field_bor_eq("); + object.writeJavaCode(mainblock, writer); + writer.addCode(", \"" + field.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + + public void compileBitAndEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert (object.isLeftValue() && !object.nullable()); + + writer.addCode("field_band_eq("); + object.writeJavaCode(mainblock, writer); + writer.addCode(", \"" + field.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + + public void compileBitXorEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert (object.isLeftValue() && !object.nullable()); + + writer.addCode("field_bxor_eq("); + object.writeJavaCode(mainblock, writer); + writer.addCode(", \"" + field.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + + public void compileShiftLeftEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert (object.isLeftValue() && !object.nullable()); + + writer.addCode("field_shl_eq("); + object.writeJavaCode(mainblock, writer); + writer.addCode(", \"" + field.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + + @Override + + public void compileShiftRightEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert (object.isLeftValue() && !object.nullable()); + + writer.addCode("field_shr_eq("); + object.writeJavaCode(mainblock, writer); + writer.addCode(", \"" + field.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + + @Override + + public void compileShiftUnsignedRightEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert (object.isLeftValue() && !object.nullable()); + + writer.addCode("field_ushr_eq("); + object.writeJavaCode(mainblock, writer); + writer.addCode(", \"" + field.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } +} diff --git a/src/main/java/leekscript/compiler/expression/LeekParenthesis.java b/src/main/java/leekscript/compiler/expression/LeekParenthesis.java index cf71a58a..e500259d 100644 --- a/src/main/java/leekscript/compiler/expression/LeekParenthesis.java +++ b/src/main/java/leekscript/compiler/expression/LeekParenthesis.java @@ -1,6 +1,8 @@ package leekscript.compiler.expression; +import leekscript.common.Type; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.bloc.MainLeekBlock; public class LeekParenthesis extends AbstractExpression { @@ -11,29 +13,45 @@ public LeekParenthesis(AbstractExpression exp) { mExpression = exp; } + public AbstractExpression getExpression() { + return mExpression; + } + @Override - public int getType() { + public int getNature() { return 0; } + @Override + public Type getType() { + return mExpression.getType(); + } + @Override public AbstractExpression trim() { - return mExpression; + return mExpression.trim(); } @Override public String getString() { - return mExpression.getString(); + return "(" + mExpression.getString() + ")"; } @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { + writer.addCode("("); mExpression.writeJavaCode(mainblock, writer); + writer.addCode(")"); } @Override - public boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionException { - return mExpression.validExpression(mainblock); + public boolean validExpression(WordCompiler compiler, MainLeekBlock mainblock) throws LeekExpressionException { + return mExpression.validExpression(compiler, mainblock); } + @Override + public void analyze(WordCompiler compiler) { + mExpression.analyze(compiler); + operations = mExpression.getOperations(); + } } diff --git a/src/main/java/leekscript/compiler/expression/LeekString.java b/src/main/java/leekscript/compiler/expression/LeekString.java index 4a2d50f8..8f6f8645 100644 --- a/src/main/java/leekscript/compiler/expression/LeekString.java +++ b/src/main/java/leekscript/compiler/expression/LeekString.java @@ -1,6 +1,10 @@ package leekscript.compiler.expression; +import com.alibaba.fastjson.JSON; + +import leekscript.common.Type; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.bloc.MainLeekBlock; public class LeekString extends AbstractExpression { @@ -12,18 +16,23 @@ public LeekString(String str) { } @Override - public int getType() { + public int getNature() { return STRING; } + @Override + public Type getType() { + return Type.STRING; + } + @Override public String getString() { - return "\"" + mString + "\""; + return JSON.toJSONString(mString); } @Override - public boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionException { - //Pour une chaine de caractères pas de problèmes + public boolean validExpression(WordCompiler compiler, MainLeekBlock mainblock) throws LeekExpressionException { + // Pour une chaine de caractères pas de problèmes return true; } @@ -31,16 +40,33 @@ public boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionExc public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { String str = ""; int len = mString.length() - 1; - for(int i = 0; i < mString.length(); i++){ - if(mString.charAt(i) == '\n') str += "\\n"; - else if(mString.charAt(i) == '"') str += "\\\""; - else if(mString.charAt(i) == '\\'){ - if(len > i && mString.charAt(i + 1) == 'n') str += "\\"; - else if(len > i && mString.charAt(i + 1) == 't') str += "\\"; - else str += "\\\\"; + for (int i = 0; i < mString.length(); i++) { + if (mString.charAt(i) == '\n') str += "\\n"; + else if (mString.charAt(i) == '"') str += "\\\""; + else if (mString.charAt(i) == '\\') { + if (len > i && mString.charAt(i + 1) == 'n') str += "\\"; + else if (len > i && mString.charAt(i + 1) == 't') str += "\\"; + else { + if (mainblock.getCompiler().getCurrentAI().getVersion() >= 2) { + if (len > i && mString.charAt(i + 1) == '\\') { + str += "\\\\"; + i++; + } else { + str += "\\\\"; + } + } else { + // LeekScript 1.0 had a bug with "\\" strings producing 4 \ + str += "\\\\"; + } + } } else str += mString.charAt(i); } - writer.addCode("new StringLeekValue(\"" + str + "\")"); + writer.addCode("\"" + str + "\""); + } + + @Override + public void analyze(WordCompiler compiler) { + } } diff --git a/src/main/java/leekscript/compiler/expression/LeekTabularValue.java b/src/main/java/leekscript/compiler/expression/LeekTabularValue.java index 64449081..88b5a127 100644 --- a/src/main/java/leekscript/compiler/expression/LeekTabularValue.java +++ b/src/main/java/leekscript/compiler/expression/LeekTabularValue.java @@ -1,6 +1,9 @@ package leekscript.compiler.expression; +import leekscript.common.Type; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; +import leekscript.compiler.bloc.ClassMethodBlock; import leekscript.compiler.bloc.MainLeekBlock; public class LeekTabularValue extends AbstractExpression { @@ -26,46 +29,304 @@ public AbstractExpression getCase() { } @Override - public int getType() { + public int getNature() { return TABULAR_VALUE; } + @Override + public Type getType() { + return Type.ANY; + } + @Override public String getString() { return (mTabular == null ? "null" : mTabular.getString()) + "[" + (mCase == null ? "null" : mCase.getString()) + "]"; } @Override - public boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionException { - //On doit vérifier qu'on a affaire : soit à une expression tabulaire, soit à une variable, soit à une globale - if(!(mTabular instanceof LeekVariable) && !(mTabular instanceof LeekGlobal) && !(mTabular instanceof LeekTabularValue)){ - //throw new LeekExpressionException(this, "Ce n'est pas un tableau valide"); + public boolean validExpression(WordCompiler compiler, MainLeekBlock mainblock) throws LeekExpressionException { + // On doit vérifier qu'on a affaire : soit à une expression tabulaire, soit à une variable, soit à une globale + // throw new LeekExpressionException(this, "Ce n'est pas un tableau valide"); + if (!mTabular.isLeftValue()) { mLeftValue = false; } - //Sinon on valide simplement les deux expressions - mTabular.validExpression(mainblock); - mCase.validExpression(mainblock); + + // Sinon on valide simplement les deux expressions + mTabular.validExpression(compiler, mainblock); + mCase.validExpression(compiler, mainblock); return true; } @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - if(mLeftValue){ - mTabular.writeJavaCode(mainblock, writer); - writer.addCode(".getValue().getOrCreate(mUAI, "); - mCase.writeJavaCode(mainblock, writer); - writer.addCode(")"); - } - else{ - mTabular.writeJavaCode(mainblock, writer); - writer.addCode(".getValue().get(mUAI, "); - mCase.writeJavaCode(mainblock, writer); - writer.addCode(")"); - } + writer.addCode("get("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(", "); + writer.addCode(writer.currentBlock instanceof ClassMethodBlock ? "u_class" : "null"); + writer.addCode(")"); + } + + @Override + public void compileL(MainLeekBlock mainblock, JavaWriter writer) { + assert(mLeftValue && !mTabular.nullable()); + writer.addCode("getBox("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + public void compileSet(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + public void compileSetCopy(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(", "); + expr.writeJavaCode(mainblock, writer); + + // writer.compileClone(mainblock, expr); + writer.addCode(")"); + } + + @Override + public void compileIncrement(MainLeekBlock mainblock, JavaWriter writer) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put_inc("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + + @Override + public void compilePreIncrement(MainLeekBlock mainblock, JavaWriter writer) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put_pre_inc("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + public void compileDecrement(MainLeekBlock mainblock, JavaWriter writer) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put_dec("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + public void compilePreDecrement(MainLeekBlock mainblock, JavaWriter writer) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put_pre_dec("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + public void compileAddEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put_add_eq("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + public void compileSubEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put_sub_eq("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + public void compileMulEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put_mul_eq("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + public void compileModEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put_mod_eq("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + public void compileDivEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put_div_eq("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + public void compilePowEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put_pow_eq("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + public void compileBitOrEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put_bor_eq("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + + @Override + public void compileBitAndEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put_band_eq("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + + @Override + public void compileBitXorEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put_bxor_eq("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + public void compileShiftLeftEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put_shl_eq("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + public void compileShiftRightEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put_shr_eq("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + + @Override + public void compileShiftUnsignedRightEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + assert(mLeftValue && !mTabular.nullable()); + + writer.addCode("put_ushr_eq("); + mTabular.writeJavaCode(mainblock, writer); + writer.addCode(", "); + mCase.writeJavaCode(mainblock, writer); + writer.addCode(", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); } public void setLeftValue(boolean b) { mLeftValue = b; } + @Override + public boolean isLeftValue() { + return true; + } + + @Override + public boolean nullable() { + return true; + } + + @Override + public void analyze(WordCompiler compiler) { + mTabular.analyze(compiler); + mCase.analyze(compiler); + operations = mTabular.getOperations() + mCase.getOperations(); + } } diff --git a/src/main/java/leekscript/compiler/expression/LeekTernaire.java b/src/main/java/leekscript/compiler/expression/LeekTernaire.java index 29a05991..4fa97a37 100644 --- a/src/main/java/leekscript/compiler/expression/LeekTernaire.java +++ b/src/main/java/leekscript/compiler/expression/LeekTernaire.java @@ -1,14 +1,18 @@ package leekscript.compiler.expression; +import leekscript.compiler.IAWord; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.bloc.MainLeekBlock; -import leekscript.compiler.exceptions.LeekCompilerException; +import leekscript.common.Error; +import leekscript.common.Type; public class LeekTernaire extends LeekExpression { private AbstractExpression mCondition; private int mOperator = 0; + private Type type = Type.ANY; public LeekTernaire() { mCondition = null; @@ -19,19 +23,19 @@ public LeekTernaire() { @Override public boolean needOperator() { if(mCondition != null && mOperator == 0){ - if(mCondition.getType() == EXPRESSION){ + if(mCondition.getNature() == EXPRESSION){ return ((LeekExpression) mCondition).needOperator(); } return true; } if(mExpression1 != null && mOperator == 1){ - if(mExpression1.getType() == EXPRESSION){ + if(mExpression1.getNature() == EXPRESSION){ return ((LeekExpression) mExpression1).needOperator(); } return true; } if(mExpression2 != null && mOperator == 2){ - if(mExpression2.getType() == EXPRESSION){ + if(mExpression2.getNature() == EXPRESSION){ return ((LeekExpression) mExpression2).needOperator(); } return true; @@ -45,57 +49,60 @@ public boolean hasTernaire() { } @Override - public int getType() { + public int getNature() { return EXPRESSION; } - @Override - public AbstractExpression getAbstractExpression() { - return this; + public Type getType() { + return type; } @Override public String getString() { - if(mCondition instanceof LeekExpression) mCondition = ((LeekExpression) mCondition).getAbstractExpression(); - if(mExpression1 instanceof LeekExpression) mExpression1 = ((LeekExpression) mExpression1).getAbstractExpression(); - if(mExpression2 instanceof LeekExpression) mExpression2 = ((LeekExpression) mExpression2).getAbstractExpression(); - String retour = "("; + String retour = ""; retour += mCondition == null ? "null" : mCondition.getString(); retour += " ? "; retour += mExpression1 == null ? "null" : mExpression1.getString(); retour += " : "; retour += mExpression2 == null ? "null" : mExpression2.getString(); - return retour + ")"; + return retour; } @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - if(mCondition instanceof LeekExpression) mCondition = ((LeekExpression) mCondition).getAbstractExpression(); - if(mExpression1 instanceof LeekExpression) mExpression1 = ((LeekExpression) mExpression1).getAbstractExpression(); - if(mExpression2 instanceof LeekExpression) mExpression2 = ((LeekExpression) mExpression2).getAbstractExpression(); - if(!complete()) writer.addCode("/* " + getString() + " */"); - else{ - writer.addCode("("); - mCondition.writeJavaCode(mainblock, writer); - writer.addCode(".getBoolean()?("); + // if(mCondition instanceof LeekExpression) mCondition = ((LeekExpression) mCondition).getAbstractExpression(); + // if(mExpression1 instanceof LeekExpression) mExpression1 = ((LeekExpression) mExpression1).getAbstractExpression(); + // if(mExpression2 instanceof LeekExpression) mExpression2 = ((LeekExpression) mExpression2).getAbstractExpression(); + if (!complete()) writer.addCode("/* " + getString() + " */"); + else { + var branch_ops = mExpression1.operations != mExpression2.operations; + writer.getBoolean(mainblock, mCondition); + writer.addCode(" ? "); + if (mExpression1.getOperations() > 0 && branch_ops) writer.addCode("ops("); + else writer.addCode("("); mExpression1.writeJavaCode(mainblock, writer); - writer.addCode("):("); + if (mExpression1.getOperations() > 0 && branch_ops) writer.addCode(", " + mExpression1.getOperations() + ")"); + else writer.addCode(")"); + writer.addCode(" : "); + if (mExpression2.getOperations() > 0 && branch_ops) writer.addCode("ops("); + else writer.addCode("("); mExpression2.writeJavaCode(mainblock, writer); - writer.addCode("))"); + if (mExpression2.getOperations() > 0 && branch_ops) writer.addCode(", " + mExpression2.getOperations() + ")"); + else writer.addCode(")"); } } @Override - public boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionException { - if(!complete()) throw new LeekExpressionException(this, LeekCompilerException.UNCOMPLETE_EXPRESSION); - if(!(mCondition.validExpression(mainblock) && mExpression1.validExpression(mainblock) && mExpression2.validExpression(mainblock))) throw new LeekExpressionException(this, LeekCompilerException.UNCOMPLETE_EXPRESSION); + public boolean validExpression(WordCompiler compiler, MainLeekBlock mainblock) throws LeekExpressionException { + if(!complete()) throw new LeekExpressionException(this, Error.UNCOMPLETE_EXPRESSION); + if(!(mCondition.validExpression(compiler, mainblock) && mExpression1.validExpression(compiler, mainblock) && mExpression2.validExpression(compiler, mainblock))) throw new LeekExpressionException(this, Error.UNCOMPLETE_EXPRESSION); return true; } @Override public void addExpression(AbstractExpression expression) { if(mCondition == null) mCondition = expression; - else if(mCondition.getType() == EXPRESSION && !((LeekExpression) mCondition).complete()){ + else if(mCondition.getNature() == EXPRESSION && !((LeekExpression) mCondition).complete()){ ((LeekExpression) mCondition).addExpression(expression); } else if(mOperator == 1){ @@ -106,44 +113,43 @@ else if(mOperator == 2){ if(mExpression2 == null) mExpression2 = expression; else ((LeekExpression) mExpression2).addExpression(expression); } - } @Override - public void addUnarySuffix(int suffix) { + public void addUnarySuffix(int suffix, IAWord token) { //On doit ajouter ce suffix au dernier élément ajouté if(mCondition != null && mExpression1 == null && mExpression2 == null){ - if(mCondition.getType() == EXPRESSION) ((LeekExpression) mCondition).addUnarySuffix(suffix); + if(mCondition.getNature() == EXPRESSION) ((LeekExpression) mCondition).addUnarySuffix(suffix, token); else{ //On doit ajouter à l'élément mExpression1 LeekExpression exp = new LeekExpression(); exp.setParent(this); exp.setExpression1(new LeekNull()); - exp.setOperator(suffix); + exp.setOperator(suffix, token); exp.setExpression2(mCondition); mCondition = exp; } } else if(mExpression1 != null && mExpression2 == null){ - if(mExpression1.getType() == EXPRESSION) ((LeekExpression) mExpression1).addUnarySuffix(suffix); + if(mExpression1.getNature() == EXPRESSION) ((LeekExpression) mExpression1).addUnarySuffix(suffix, token); else{ //On doit ajouter à l'élément mExpression1 LeekExpression exp = new LeekExpression(); exp.setParent(this); exp.setExpression1(new LeekNull()); - exp.setOperator(suffix); + exp.setOperator(suffix, token); exp.setExpression2(mExpression1); mExpression1 = exp; } } else if(mExpression2 != null){ - if(mExpression2.getType() == EXPRESSION) ((LeekExpression) mExpression2).addUnarySuffix(suffix); + if(mExpression2.getNature() == EXPRESSION) ((LeekExpression) mExpression2).addUnarySuffix(suffix, token); else{ //On doit ajouter à l'élément mExpression2 LeekExpression exp = new LeekExpression(); exp.setParent(this); exp.setExpression1(new LeekNull()); - exp.setOperator(suffix); + exp.setOperator(suffix, token); exp.setExpression2(mExpression2); mExpression2 = exp; } @@ -161,69 +167,87 @@ public boolean complete(int operator) { public boolean complete() { if(!super.complete()) return false; if(mCondition == null) return false; - if(mCondition.getType() == EXPRESSION && !((LeekExpression) mCondition).complete()) return false; + if(mCondition.getNature() == EXPRESSION && !((LeekExpression) mCondition).complete()) return false; return true; } @Override - public void addOperator(int operator) { + public void addOperator(int operator, IAWord token) { //On doit trouver à quel endroit de l'arborescence on doit placer l'opérateur if(mOperator == 0 && operator == Operators.TERNAIRE){ mOperator = 1; } - else if(mExpression1.getType() == EXPRESSION && !((LeekExpression) mExpression1).complete()) ((LeekExpression) mExpression1).addOperator(operator); + else if(mExpression1.getNature() == EXPRESSION && !((LeekExpression) mExpression1).complete()) ((LeekExpression) mExpression1).addOperator(operator, token); else if(mOperator == 1 && operator == Operators.DOUBLE_POINT){ mOperator = 2; } else{ if(mOperator == 0){ - if(mCondition.getType() == EXPRESSION) ((LeekExpression) mCondition).addOperator(operator); + if(mCondition.getNature() == EXPRESSION) ((LeekExpression) mCondition).addOperator(operator, token); else{ LeekExpression new_e = new LeekExpression(); new_e.setParent(this); new_e.setExpression1(mCondition); - new_e.setOperator(operator); + new_e.setOperator(operator, token); mCondition = new_e; } } else if(mOperator == 1){ - if(mExpression1.getType() == EXPRESSION) ((LeekExpression) mExpression1).addOperator(operator); + if(mExpression1.getNature() == EXPRESSION) ((LeekExpression) mExpression1).addOperator(operator, token); else{ if(operator == Operators.TERNAIRE){ LeekTernaire new_e = new LeekTernaire(); new_e.setParent(this); new_e.addExpression(mExpression1); - new_e.addOperator(operator); + new_e.addOperator(operator, token); mExpression1 = new_e; } else{ LeekExpression new_e = new LeekExpression(); new_e.setParent(this); new_e.setExpression1(mExpression1); - new_e.setOperator(operator); + new_e.setOperator(operator, token); mExpression1 = new_e; } } } else{ - if(mExpression2.getType() == EXPRESSION) ((LeekExpression) mExpression2).addOperator(operator); + if(mExpression2.getNature() == EXPRESSION) ((LeekExpression) mExpression2).addOperator(operator, token); else{ if(operator == Operators.TERNAIRE){ LeekTernaire new_e = new LeekTernaire(); new_e.setParent(this); new_e.addExpression(mExpression2); - new_e.addOperator(operator); + new_e.addOperator(operator, token); mExpression2 = new_e; } else{ LeekExpression new_e = new LeekExpression(); new_e.setParent(this); new_e.setExpression1(mExpression2); - new_e.setOperator(operator); + new_e.setOperator(operator, token); mExpression2 = new_e; } } } } } + + @Override + public void analyze(WordCompiler compiler) { + if (mCondition != null) { + mCondition.analyze(compiler); + operations = 1 + mCondition.getOperations(); + } + mExpression1.analyze(compiler); + mExpression2.analyze(compiler); + + if (mExpression1.operations == mExpression2.operations) { + operations += mExpression1.operations; + } + + if (mExpression1 != null && mExpression2 != null && mExpression1.getType() == mExpression2.getType()) { + type = mExpression1.getType(); + } + } } diff --git a/src/main/java/leekscript/compiler/expression/LeekVariable.java b/src/main/java/leekscript/compiler/expression/LeekVariable.java index 1982911f..c1642d03 100644 --- a/src/main/java/leekscript/compiler/expression/LeekVariable.java +++ b/src/main/java/leekscript/compiler/expression/LeekVariable.java @@ -1,33 +1,859 @@ package leekscript.compiler.expression; +import leekscript.compiler.AnalyzeError; +import leekscript.compiler.IAWord; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; +import leekscript.compiler.AnalyzeError.AnalyzeErrorLevel; +import leekscript.compiler.bloc.FunctionBlock; import leekscript.compiler.bloc.MainLeekBlock; +import leekscript.compiler.instruction.ClassDeclarationInstruction; +import leekscript.compiler.instruction.LeekVariableDeclarationInstruction; +import leekscript.runner.LeekConstants; +import leekscript.runner.LeekFunctions; +import leekscript.common.Error; +import leekscript.common.Type; public class LeekVariable extends AbstractExpression { - private final String mVariable; + public static enum VariableType { + LOCAL, GLOBAL, ARGUMENT, FIELD, STATIC_FIELD, THIS, THIS_CLASS, CLASS, SUPER, METHOD, STATIC_METHOD, SYSTEM_CONSTANT, SYSTEM_FUNCTION, FUNCTION, ITERATOR + } + + private final IAWord token; + private VariableType type; + private Type variableType = Type.ANY; + private LeekVariableDeclarationInstruction declaration; + private ClassDeclarationInstruction classDeclaration; + private boolean box; + + public LeekVariable(IAWord token, VariableType type) { + this.token = token; + this.type = type; + this.declaration = null; + this.classDeclaration = null; + this.box = false; + } - public LeekVariable(String variable) { - mVariable = variable; + public LeekVariable(WordCompiler compiler, IAWord token, VariableType type) { + this.token = token; + this.type = type; + this.declaration = null; + this.classDeclaration = null; + this.box = compiler.getVersion() <= 1; + } + + public LeekVariable(IAWord token, VariableType type, boolean box) { + this.token = token; + this.type = type; + this.declaration = null; + this.classDeclaration = null; + this.box = box; + } + + public LeekVariable(IAWord token, VariableType type, LeekVariableDeclarationInstruction declaration) { + this.token = token; + this.type = type; + this.declaration = declaration; + this.classDeclaration = null; + this.box = declaration.isCaptured(); + } + + public LeekVariable(IAWord token, VariableType type, ClassDeclarationInstruction classDeclaration) { + this.token = token; + this.type = type; + this.classDeclaration = classDeclaration; + this.box = false; } @Override - public int getType() { + public int getNature() { return VARIABLE; } + @Override + public Type getType() { + return variableType; + } + @Override public String getString() { - return mVariable; + return token.getWord(); } @Override - public boolean validExpression(MainLeekBlock mainblock) throws LeekExpressionException { + public boolean validExpression(WordCompiler compiler, MainLeekBlock mainblock) throws LeekExpressionException { return true; } + public boolean isLeftValue() { + if (type == VariableType.CLASS || type == VariableType.THIS || type == VariableType.THIS_CLASS || type == VariableType.SUPER || type == VariableType.SYSTEM_CONSTANT) { + return false; + } + return true; + } + + @Override + public boolean nullable() { + // return type != VariableType.CLASS && type != VariableType.THIS && type != VariableType.THIS_CLASS; + return false; + } + + public VariableType getVariableType() { + return type; + } + + public void setVariableType(VariableType type) { + this.type = type; + } + + public String getName() { + return token.getWord(); + } + + @Override + public void analyze(WordCompiler compiler) { + if (this.type == VariableType.SUPER) { + return; // Déjà OK + } + // Local variables first + var v = compiler.getCurrentBlock().getVariable(token.getWord(), true); + if (v != null) { + this.type = v.getVariableType(); + this.declaration = v.getDeclaration(); + this.classDeclaration = v.getClassDeclaration(); + this.box = v.box; + if (v.getDeclaration() != null && v.getDeclaration().getFunction() != compiler.getCurrentFunction()) { + v.getDeclaration().setCaptured(); + } + if (this.type == VariableType.FIELD) { + operations += 1; + } + return; + } + // Global user functions + if (compiler.getMainBlock().hasUserFunction(token.getWord(), true)) { + this.type = VariableType.FUNCTION; + return; + } + // LS constants + var constant = LeekConstants.get(token.getWord()); + if (constant != null) { + this.type = VariableType.SYSTEM_CONSTANT; + this.variableType = constant.getType(); + return; + } + // LS functions + if (LeekFunctions.isFunction(token.getWord()) != -1) { + this.type = VariableType.SYSTEM_FUNCTION; + return; + } + compiler.addError(new AnalyzeError(token, AnalyzeErrorLevel.ERROR, Error.UNKNOWN_VARIABLE_OR_FUNCTION)); + } + + public ClassDeclarationInstruction getClassDeclaration() { + return classDeclaration; + } + + public LeekVariableDeclarationInstruction getDeclaration() { + return declaration; + } + + public IAWord getToken() { + return token; + } + + public boolean isBox() { + return this.box || (declaration != null && declaration.isBox()); + } + + public boolean isWrapper() { + return declaration != null && declaration.isWrapper(); + } + @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - writer.addCode("user_" + mVariable); + if (type == VariableType.THIS) { + writer.addCode("u_this"); + } else if (type == VariableType.THIS_CLASS) { + writer.addCode("u_class"); + } else if (type == VariableType.SUPER) { + writer.addCode("u_" + classDeclaration.getParent().getName()); + } else if (type == VariableType.FIELD) { + writer.addCode("u_this.getField(\"" + token.getWord() + "\")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.getField(\"" + token.getWord() + "\")"); + } else if (type == VariableType.METHOD) { + writer.addCode("u_class.getField(\"" + token.getWord() + "\")"); + } else if (type == VariableType.STATIC_METHOD) { + writer.addCode("u_class.getField(\"" + token.getWord() + "\")"); + } else if (mainblock.isRedefinedFunction(token.getWord())) { + writer.addCode("rfunction_" + token.getWord()); + } else if (type == VariableType.FUNCTION) { + FunctionBlock user_function = mainblock.getUserFunction(token.getWord()); + writer.addCode("new FunctionLeekValue(" + user_function.getId() + ")"); + } else if (type == VariableType.SYSTEM_CONSTANT) { + var constant = LeekConstants.get(token.getWord()); + // if (constant.getType() == LeekFunctions.INT) writer.addCode("LeekValueManager.getLeekIntValue(" + constant.getIntValue() + ")"); + if (constant.getType() == Type.INT) writer.addCode(String.valueOf(constant.getIntValue())); + // else if (constant.getType() == LeekFunctions.DOUBLE) writer.addCode("new DoubleLeekValue(" + constant.getValue() + ")"); + else if (constant.getType() == Type.REAL) writer.addCode(String.valueOf(constant.getValue())); + else writer.addCode("null"); + } else if (type == VariableType.SYSTEM_FUNCTION) { + FunctionBlock user_function = mainblock.getUserFunction(token.getWord()); + if (user_function != null) { + writer.addCode("new FunctionLeekValue(" + user_function.getId() + ")"); + } else { + String namespace = LeekFunctions.getNamespace(token.getWord()); + writer.addCode("LeekValueManager.getFunction(" + namespace + "." + token.getWord() + ")"); + } + } else if (type == VariableType.GLOBAL) { + if (mainblock.getWordCompiler().getVersion() <= 1) { + writer.addCode("g_" + token.getWord() + ".getValue()"); + } else { + writer.addCode("g_" + token.getWord()); + } + } else if (type == VariableType.CLASS) { + if (classDeclaration.internal) { + writer.addCode(token.getWord().toLowerCase() + "Class"); + } else { + writer.addCode("u_" + token.getWord()); + } + } else { + if (isWrapper()) { + writer.addCode("u_" + token.getWord() + ".getValue()"); + } else if (isBox()) { + writer.addCode("u_" + token.getWord() + ".getValue()"); + } else { + writer.addCode("u_" + token.getWord()); + } + } + } + + @Override + public void compileL(MainLeekBlock mainblock, JavaWriter writer) { + if (type == VariableType.THIS) { + writer.addCode("u_this"); + } else if (type == VariableType.THIS_CLASS) { + writer.addCode("u_class"); + } else if (type == VariableType.SUPER) { + writer.addCode("u_" + classDeclaration.getParent().getName()); + } else if (type == VariableType.FIELD) { + writer.addCode("u_this.getFieldL(\"" + token.getWord() + "\")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.getFieldL(\"" + token.getWord() + "\")"); + } else if (type == VariableType.GLOBAL) { + writer.addCode("g_" + token.getWord()); + } else if (mainblock.isRedefinedFunction(token.getWord())) { + writer.addCode("rfunction_" + token.getWord()); + } else if (type == VariableType.SYSTEM_FUNCTION) { + FunctionBlock user_function = mainblock.getUserFunction(token.getWord()); + if (user_function != null) { + writer.addCode("new FunctionLeekValue(" + user_function.getId() + ")"); + } else { + String namespace = LeekFunctions.getNamespace(token.getWord()); + writer.addCode("LeekValueManager.getFunction(" + namespace + "." + token.getWord() + ")"); + } + } else if (type == VariableType.SYSTEM_CONSTANT) { + var constant = LeekConstants.get(token.getWord()); + if (constant.getType() == Type.INT) writer.addCode(String.valueOf(constant.getIntValue())); + else if (constant.getType() == Type.REAL) writer.addCode(String.valueOf(constant.getValue())); + else writer.addCode("null"); + } else if (type == VariableType.FUNCTION) { + FunctionBlock user_function = mainblock.getUserFunction(token.getWord()); + writer.addCode("new FunctionLeekValue(" + user_function.getId() + ")"); + } else if (type == VariableType.CLASS) { + if (classDeclaration.internal) { + writer.addCode(token.getWord().toLowerCase() + "Class"); + } else { + writer.addCode("u_" + token.getWord()); + } + } else { + if (isWrapper()) { + writer.addCode("u_" + token.getWord() + ".getVariable()"); + } else { + writer.addCode("u_" + token.getWord()); + } + } + } + + @Override + public void compileSet(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.setField(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.setField(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (mainblock.isRedefinedFunction(token.getWord())) { + writer.addCode("rfunction_" + token.getWord() + ".set("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.GLOBAL) { + if (mainblock.getWordCompiler().getVersion() >= 2) { + writer.addCode("g_" + token.getWord() + " = "); + expr.writeJavaCode(mainblock, writer); + } else { + writer.addCode("g_" + token.getWord() + ".set("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } else { + if (isWrapper()) { + if (expr.isLeftValue()) { + if (mainblock.getWordCompiler().getVersion() <= 1) { + writer.addCode("u_" + token.getWord() + ".setBox("); + } else { + writer.addCode("u_" + token.getWord() + ".set("); + } + expr.compileL(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("u_" + token.getWord() + ".setBoxOrValue("); + expr.compileL(mainblock, writer); + writer.addCode(")"); + } + } else if (isBox()) { + // if (expr.isLeftValue()) { + // writer.addCode("u_" + token.getWord() + " = "); + // expr.compileL(mainblock, writer); + // } else { + writer.addCode("u_" + token.getWord() + ".set("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + // } + } else { + writer.addCode("u_" + token.getWord() + " = "); + expr.writeJavaCode(mainblock, writer); + } + } + } + + @Override + public void compileSetCopy(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.setField(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.setField(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (mainblock.isRedefinedFunction(token.getWord())) { + writer.addCode("rfunction_" + token.getWord() + ".set("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.GLOBAL) { + if (mainblock.getWordCompiler().getVersion() >= 2) { + writer.addCode("g_" + token.getWord() + " = "); + // writer.compileClone(mainblock, expr); + expr.writeJavaCode(mainblock, writer); + } else { + writer.addCode("g_" + token.getWord() + ".set("); + // writer.compileClone(mainblock, expr); + expr.compileL(mainblock, writer); + writer.addCode(")"); + } + } else { + if (isWrapper() || isBox()) { + writer.addCode("u_" + token.getWord() + ".set("); + // writer.compileClone(mainblock, expr); + // expr.writeJavaCode(mainblock, writer); + expr.compileL(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("u_" + token.getWord() + " = "); + // writer.compileClone(mainblock, expr); + expr.writeJavaCode(mainblock, writer); + } + } + } + + @Override + public void compileIncrement(MainLeekBlock mainblock, JavaWriter writer) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.field_inc(\"" + token.getWord() + "\")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.field_inc(\"" + token.getWord() + "\")"); + } else if (type == VariableType.GLOBAL) { + if (isBox()) { + writer.addCode("g_" + token.getWord() + ".increment()"); + } else { + writer.addCode("sub(g_" + token.getWord() + " = add(g_" + token.getWord() + ", 1), 1)"); + } + } else { + if (isBox()) { + writer.addCode("u_" + token.getWord() + ".increment()"); + } else { + writer.addCode("sub(u_" + token.getWord() + " = add(u_" + token.getWord() + ", 1), 1)"); + } + } + } + + @Override + public void compileDecrement(MainLeekBlock mainblock, JavaWriter writer) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.field_dec(\"" + token.getWord() + "\")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.field_dec(\"" + token.getWord() + "\")"); + } else if (type == VariableType.GLOBAL) { + if (isBox()) { + writer.addCode("g_" + token.getWord() + ".decrement()"); + } else { + writer.addCode("add(g_" + token.getWord() + " = sub(g_" + token.getWord() + ", 1), 1)"); + } + } else { + if (isBox()) { + writer.addCode("u_" + token.getWord() + ".decrement()"); + } else { + writer.addCode("add(u_" + token.getWord() + " = sub(u_" + token.getWord() + ", 1), 1)"); + } + } + } + + @Override + public void compilePreIncrement(MainLeekBlock mainblock, JavaWriter writer) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.field_pre_inc(\"" + token.getWord() + "\")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.field_pre_inc(\"" + token.getWord() + "\")"); + } else if (type == VariableType.GLOBAL) { + if (isBox()) { + writer.addCode("g_" + token.getWord() + ".pre_increment()"); + } else { + writer.addCode("g_" + token.getWord() + " = add(g_" + token.getWord() + ", 1)"); + } + } else { + if (isBox()) { + writer.addCode("u_" + token.getWord() + ".pre_increment()"); + } else { + writer.addCode("u_" + token.getWord() + " = add(u_" + token.getWord() + ", 1)"); + } + } + } + + @Override + public void compilePreDecrement(MainLeekBlock mainblock, JavaWriter writer) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.field_pre_dec(\"" + token.getWord() + "\")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.field_pre_dec(\"" + token.getWord() + "\")"); + } else if (type == VariableType.GLOBAL) { + if (isBox()) { + writer.addCode("g_" + token.getWord() + ".pre_decrement()"); + } else { + writer.addCode("g_" + token.getWord() + " = sub(g_" + token.getWord() + ", 1)"); + } + } else { + if (isBox()) { + writer.addCode("u_" + token.getWord() + ".pre_decrement()"); + } else { + writer.addCode("u_" + token.getWord() + " = sub(u_" + token.getWord() + ", 1)"); + } + } + } + + @Override + public void compileAddEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.field_add_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.field_add_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.GLOBAL) { + if (isBox()) { + writer.addCode("g_" + token.getWord() + ".add_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("g_" + token.getWord() + " = add(g_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } else { + if (isBox()) { + writer.addCode("u_" + token.getWord() + ".add_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + // writer.addCode("u_" + token.getWord() + " = add(u_" + token.getWord() + ", "); + // expr.writeJavaCode(mainblock, writer); + // writer.addCode(")"); + writer.addCode("u_" + token.getWord() + " = add_eq(u_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } + } + + @Override + public void compileSubEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.field_sub_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.field_sub_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.GLOBAL) { + if (isBox()) { + writer.addCode("g_" + token.getWord() + ".sub_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("g_" + token.getWord() + " = sub(g_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } else { + if (isBox()) { + writer.addCode("u_" + token.getWord() + ".sub_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("u_" + token.getWord() + " = sub(u_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } + } + + @Override + public void compileMulEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.field_mul_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.field_mul_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.GLOBAL) { + if (isBox()) { + writer.addCode("g_" + token.getWord() + ".mul_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("g_" + token.getWord() + " = mul(g_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } else { + if (isBox()) { + writer.addCode("u_" + token.getWord() + ".mul_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("u_" + token.getWord() + " = mul(u_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } + } + + + @Override + public void compilePowEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.field_pow_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.field_pow_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.GLOBAL) { + if (isBox()) { + writer.addCode("g_" + token.getWord() + ".pow_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("g_" + token.getWord() + " = pow(g_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } else { + if (isBox()) { + writer.addCode("u_" + token.getWord() + ".pow_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("u_" + token.getWord() + " = pow(u_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } + } + + @Override + public void compileDivEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.field_div_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.field_div_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.GLOBAL) { + if (isBox()) { + writer.addCode("g_" + token.getWord() + ".div_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("g_" + token.getWord() + " = div(g_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } else { + if (isBox()) { + writer.addCode("u_" + token.getWord() + ".div_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("u_" + token.getWord() + " = div(u_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } + } + + @Override + public void compileModEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.field_mod_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.field_mod_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.GLOBAL) { + if (isBox()) { + writer.addCode("g_" + token.getWord() + ".mod_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("g_" + token.getWord() + " = mod(g_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } else { + if (isBox()) { + writer.addCode("u_" + token.getWord() + ".mod_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("u_" + token.getWord() + " = mod(u_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } + } + + @Override + public void compileBitOrEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.field_bor_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.field_bor_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.GLOBAL) { + if (isBox()) { + writer.addCode("g_" + token.getWord() + ".bor_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("g_" + token.getWord() + " = bor(g_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } else { + if (isBox()) { + writer.addCode("u_" + token.getWord() + ".bor_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("u_" + token.getWord() + " = bor(u_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } + } + + + @Override + public void compileBitAndEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.field_band_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.field_band_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.GLOBAL) { + if (isBox()) { + writer.addCode("g_" + token.getWord() + ".band_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("g_" + token.getWord() + " = band(g_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } else { + if (isBox()) { + writer.addCode("u_" + token.getWord() + ".band_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("u_" + token.getWord() + " = band(u_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } + } + + + @Override + public void compileBitXorEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.field_bxor_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.field_bxor_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.GLOBAL) { + if (isBox()) { + writer.addCode("g_" + token.getWord() + ".bxor_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("g_" + token.getWord() + " = bxor(g_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } else { + if (isBox()) { + writer.addCode("u_" + token.getWord() + ".bxor_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("u_" + token.getWord() + " = bxor(u_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } + } + + @Override + public void compileShiftLeftEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.field_shl_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.field_shl_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.GLOBAL) { + if (isBox()) { + writer.addCode("g_" + token.getWord() + ".shl_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("g_" + token.getWord() + " = shl(g_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } else { + if (isBox()) { + writer.addCode("u_" + token.getWord() + ".shl_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("u_" + token.getWord() + " = shl(u_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } + } + + @Override + public void compileShiftRightEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.field_shr_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.field_shr_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.GLOBAL) { + if (isBox()) { + writer.addCode("g_" + token.getWord() + ".shr_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("g_" + token.getWord() + " = shr(g_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } else { + if (isBox()) { + writer.addCode("u_" + token.getWord() + ".shr_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("u_" + token.getWord() + " = shr(u_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } + } + + @Override + public void compileShiftUnsignedRightEq(MainLeekBlock mainblock, JavaWriter writer, AbstractExpression expr) { + if (type == VariableType.FIELD) { + writer.addCode("u_this.field_ushr_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.STATIC_FIELD) { + writer.addCode("u_class.field_ushr_eq(\"" + token.getWord() + "\", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else if (type == VariableType.GLOBAL) { + if (isBox()) { + writer.addCode("g_" + token.getWord() + ".ushr_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("g_" + token.getWord() + " = ushr(g_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } else { + if (isBox()) { + writer.addCode("u_" + token.getWord() + ".ushr_eq("); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } else { + writer.addCode("u_" + token.getWord() + " = ushr(u_" + token.getWord() + ", "); + expr.writeJavaCode(mainblock, writer); + writer.addCode(")"); + } + } } } diff --git a/src/main/java/leekscript/compiler/expression/Operators.java b/src/main/java/leekscript/compiler/expression/Operators.java index 12f70109..bc41c6c7 100644 --- a/src/main/java/leekscript/compiler/expression/Operators.java +++ b/src/main/java/leekscript/compiler/expression/Operators.java @@ -33,13 +33,13 @@ public class Operators { public final static int EQUALS_EQUALS = 30; public final static int NOT_EQUALS_EQUALS = 31; public final static int SHIFT_LEFT = 32; - public final static int ROTATE_RIGHT = 33; + public final static int SHIFT_UNSIGNED_RIGHT = 33; public final static int SHIFT_RIGHT = 34; public final static int BITXOR = 35; public final static int BITAND = 36; public final static int BITOR = 37; public final static int SHIFT_LEFT_ASSIGN = 38; - public final static int ROTATE_RIGHT_ASSIGN = 39; + public final static int SHIFT_UNSIGNED_RIGHT_ASSIGN = 39; public final static int SHIFT_RIGHT_ASSIGN = 40; public final static int BITXOR_ASSIGN = 41; public final static int BITAND_ASSIGN = 42; @@ -47,8 +47,12 @@ public class Operators { public final static int PRE_INCREMENT = 44; public final static int PRE_DECREMENT = 45; public final static int REFERENCE = 46; + public final static int BITNOT = 47; + public final static int NEW = 48; + public final static int DOT = 49; + public final static int INSTANCEOF = 50; - public final static int getOperator(String operator) { + public final static int getOperator(String operator, int version) { if(operator.equals("[")) return CROCHET; if(operator.equals("(")) return PARENTHESIS; if(operator.equals("++")) return INCREMENT; @@ -76,23 +80,33 @@ public final static int getOperator(String operator) { if(operator.equals("?")) return TERNAIRE; if(operator.equals(":")) return DOUBLE_POINT; if(operator.equals("**")) return POWER; - if(operator.equals("^=")) return POWERASSIGN; + if(operator.equals("**=")) return POWERASSIGN; if(operator.equals("===")) return EQUALS_EQUALS; if(operator.equals("!==")) return NOT_EQUALS_EQUALS; if(operator.equals("^")) return BITXOR; + if(operator.equals("^=")) { + if (version >= 2) { + return BITXOR_ASSIGN; + } else { + // In LeekScript 1.0, ^= was still power assignment + return POWERASSIGN; + } + } if(operator.equals("&")) return BITAND; if(operator.equals("|")) return BITOR; + if(operator.equals("~")) return BITNOT; if(operator.equals("<<")) return SHIFT_LEFT; if(operator.equals(">>")) return SHIFT_RIGHT; - if(operator.equals(">>>")) return ROTATE_RIGHT; - if(operator.equals("**=")) return POWERASSIGN; - if(operator.equals("^=")) return BITXOR_ASSIGN; + if(operator.equals(">>>")) return SHIFT_UNSIGNED_RIGHT; if(operator.equals("&=")) return BITAND_ASSIGN; if(operator.equals("|=")) return BITOR_ASSIGN; if(operator.equals("<<=")) return SHIFT_LEFT_ASSIGN; if(operator.equals(">>=")) return SHIFT_RIGHT_ASSIGN; - if(operator.equals(">>>=")) return ROTATE_RIGHT_ASSIGN; + if(operator.equals(">>>=")) return SHIFT_UNSIGNED_RIGHT_ASSIGN; if(operator.equals("@")) return REFERENCE; + if(operator.equals("new")) return NEW; + if(operator.equals(".")) return DOT; + if(operator.equals("instanceof")) return INSTANCEOF; return -1; } @@ -100,13 +114,18 @@ public static int getPriority(int operator) { switch(operator) { case CROCHET: case PARENTHESIS: - return 15; + return 17; + case DOT: + return 16; case INCREMENT: case DECREMENT: case REFERENCE: case UNARY_MINUS: - return 14; + return 15; case NOT: + case BITNOT: + return 14; + case NEW: return 13; case POWER: return 12; @@ -119,12 +138,13 @@ public static int getPriority(int operator) { return 10; case SHIFT_LEFT: case SHIFT_RIGHT: - case ROTATE_RIGHT: + case SHIFT_UNSIGNED_RIGHT: return 9; case LESS: case LESSEQUALS: case MORE: case MOREEQUALS: + case INSTANCEOF: return 8; case EQUALS: case NOTEQUALS: @@ -153,7 +173,7 @@ public static int getPriority(int operator) { case POWERASSIGN: case SHIFT_LEFT_ASSIGN: case SHIFT_RIGHT_ASSIGN: - case ROTATE_RIGHT_ASSIGN: + case SHIFT_UNSIGNED_RIGHT_ASSIGN: case BITAND_ASSIGN: case BITOR_ASSIGN: case BITXOR_ASSIGN: @@ -170,6 +190,8 @@ public static boolean isUnaryPrefix(int operator) { case PRE_INCREMENT: case PRE_DECREMENT: case REFERENCE: + case BITNOT: + case NEW: return true; } return false; @@ -188,16 +210,25 @@ public static boolean isUnarySuffix(int operator) { return false; } - public static String getString(int operator) { - switch(operator) { + public static String getString(int operator, int version) { + switch (operator) { case ADD: return "+"; + case ADDASSIGN: + return "+="; case MINUS: + case UNARY_MINUS: return "-"; + case MINUSASSIGN: + return "-="; case MULTIPLIE: return "*"; + case MULTIPLIEASSIGN: + return "*="; case DIVIDE: return "/"; + case DIVIDEASSIGN: + return "/="; case MODULUS: return "%"; case ASSIGN: @@ -206,20 +237,66 @@ public static String getString(int operator) { return "&&"; case OR: return "||"; + case LESS: + return "<"; + case LESSEQUALS: + return "<="; + case MORE: + return ">"; + case MOREEQUALS: + return ">="; case EQUALS: return "=="; + case EQUALS_EQUALS: + return "==="; case NOTEQUALS: return "!="; + case NOT_EQUALS_EQUALS: + return "!=="; case NOT: return "!"; + case BITAND: + return "&"; + case BITAND_ASSIGN: + return "&="; + case BITOR: + return "|"; + case BITOR_ASSIGN: + return "|="; + case BITXOR: + return "^"; + case BITXOR_ASSIGN: + return "^="; + case SHIFT_LEFT: + return "<<"; + case SHIFT_LEFT_ASSIGN: + return "<<="; + case SHIFT_RIGHT: + return ">>"; + case SHIFT_RIGHT_ASSIGN: + return ">>="; + case SHIFT_UNSIGNED_RIGHT: + return ">>>"; + case SHIFT_UNSIGNED_RIGHT_ASSIGN: + return ">>>="; case INCREMENT: + case PRE_INCREMENT: return "++"; case DECREMENT: + case PRE_DECREMENT: return "--"; case POWER: - return "^"; + return "**"; case POWERASSIGN: - return "^="; + return version == 1 ? "^=" : "**="; + case NEW: + return "new"; + case BITNOT: + return "~"; + case REFERENCE: + return "@"; + case INSTANCEOF: + return "instanceof"; } return "null"; } diff --git a/src/main/java/leekscript/compiler/instruction/BlankInstruction.java b/src/main/java/leekscript/compiler/instruction/BlankInstruction.java index b60e0d15..3d013c85 100644 --- a/src/main/java/leekscript/compiler/instruction/BlankInstruction.java +++ b/src/main/java/leekscript/compiler/instruction/BlankInstruction.java @@ -1,6 +1,7 @@ package leekscript.compiler.instruction; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.bloc.MainLeekBlock; public class BlankInstruction implements LeekInstruction { @@ -24,4 +25,13 @@ public boolean putCounterBefore() { return false; } + @Override + public void analyze(WordCompiler compiler) { + + } + + @Override + public int getOperations() { + return 0; + } } diff --git a/src/main/java/leekscript/compiler/instruction/ClassDeclarationInstruction.java b/src/main/java/leekscript/compiler/instruction/ClassDeclarationInstruction.java new file mode 100644 index 00000000..857381a7 --- /dev/null +++ b/src/main/java/leekscript/compiler/instruction/ClassDeclarationInstruction.java @@ -0,0 +1,640 @@ +package leekscript.compiler.instruction; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map.Entry; + +import leekscript.compiler.AIFile; +import leekscript.compiler.AnalyzeError; +import leekscript.compiler.IAWord; +import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; +import leekscript.compiler.AnalyzeError.AnalyzeErrorLevel; +import leekscript.compiler.bloc.ClassMethodBlock; +import leekscript.compiler.bloc.MainLeekBlock; +import leekscript.compiler.exceptions.LeekCompilerException; +import leekscript.compiler.expression.AbstractExpression; +import leekscript.compiler.expression.LeekVariable; +import leekscript.compiler.expression.LeekVariable.VariableType; +import leekscript.common.AccessLevel; +import leekscript.common.Error; + +public class ClassDeclarationInstruction implements LeekInstruction { + + public static class ClassDeclarationField { + + AbstractExpression expression; + AccessLevel level; + + public ClassDeclarationField(AbstractExpression expr, AccessLevel level) { + this.expression = expr; + this.level = level; + } + } + + public static class ClassDeclarationMethod { + + public ClassMethodBlock block; + public AccessLevel level; + + public ClassDeclarationMethod(ClassMethodBlock block, AccessLevel level) { + this.block = block; + this.level = level; + } + } + + private final IAWord token; + private IAWord parentToken; + private ClassDeclarationInstruction parent; + public boolean internal; + private LinkedHashMap fields = new LinkedHashMap<>(); + private LinkedHashMap staticFields = new LinkedHashMap<>(); + private HashMap fieldVariables = new HashMap<>(); + private HashMap staticFieldVariables = new HashMap<>(); + private HashMap methodVariables = new HashMap<>(); + private HashMap staticMethodVariables = new HashMap<>(); + private HashMap constructors = new HashMap<>(); + private HashMap> methods = new HashMap<>(); + private HashMap> staticMethods = new HashMap<>(); + + public ClassDeclarationInstruction(IAWord token, int line, AIFile ai, boolean internal) { + this.token = token; + this.internal = internal; + } + + public HashMap getFields() { + return fields; + } + public HashMap getStaticFields() { + return staticFields; + } + + public HashMap getFieldVariables() { + return fieldVariables; + } + public HashMap getMethodVariables() { + return methodVariables; + } + public HashMap getStaticFieldVariables() { + return staticFieldVariables; + } + public HashMap getStaticMethodVariables() { + return staticMethodVariables; + } + + public String getName() { + return token.getWord(); + } + + @Override + public String getCode() { + String r = "class " + token.getWord(); + if (parentToken != null) { + r += " extends " + parentToken.getWord(); + } + r += " {\n"; + + for (Entry field : staticFields.entrySet()) { + r += "\t" + field.getValue().level.toString().toLowerCase() + " static " + field.getKey(); + if (field.getValue().expression != null) { + r += " = " + field.getValue().expression.getString(); + } + r += "\n"; + } + r += "\n"; + + for (var method : staticMethods.entrySet()) { + for (var version : method.getValue().entrySet()) { + r += "\t" + version.getValue().level.toString().toLowerCase() + " static " + method.getKey() + version.getValue().block.getCode(); + } + r += "\n"; + } + r += "\n"; + + for (Entry field : fields.entrySet()) { + r += "\t" + field.getValue().level.toString().toLowerCase() + " " + field.getKey(); + if (field.getValue().expression != null) { + r += " = " + field.getValue().expression.getString(); + } + r += "\n"; + } + r += "\n"; + + for (var constructor : constructors.entrySet()) { + r += "\t" + constructor.getValue().level.toString().toLowerCase() + " constructor" + constructor.getValue().block.getCode(); + } + r += "\n"; + + for (var method : methods.entrySet()) { + for (var version : method.getValue().entrySet()) { + r += "\t" + version.getValue().level.toString().toLowerCase() + " " + method.getKey() + version.getValue().block.getCode(); + } + r += "\n"; + } + + r += "}"; + return r; + } + + @Override + public int getEndBlock() { + return 0; + } + + @Override + public boolean putCounterBefore() { + return false; + } + + public void setParent(IAWord userClass) { + this.parentToken = userClass; + } + + public boolean hasConstructor(int param_count) { + return constructors.containsKey(param_count); + } + + public ClassDeclarationMethod getConstructor(int param_count) { + // Search existing constructor + var constructor = constructors.get(param_count); + if (constructor != null) return constructor; + + // If constructor has 0 parameters, return the default implicit one + if (param_count == 0) { + return new ClassDeclarationMethod(null, AccessLevel.PUBLIC); + } + return null; + } + + public void addConstructor(ClassMethodBlock block, AccessLevel level) { + constructors.put(block.countParameters(), new ClassDeclarationMethod(block, level)); + } + + public void addMethod(WordCompiler compiler, IAWord token, ClassMethodBlock method, AccessLevel level) { + // On regarde si il n'y a pas déjà une méthode statique du même nom + if (staticMethods.containsKey(token.getWord())) { + compiler.addError(new AnalyzeError(token, AnalyzeErrorLevel.ERROR, Error.DUPLICATED_METHOD)); + } + if (!methods.containsKey(token.getWord())) { + methods.put(token.getWord(), new HashMap<>()); + methodVariables.put(token.getWord(), new LeekVariable(token, VariableType.METHOD)); + } + methods.get(token.getWord()).put(method.countParameters(), new ClassDeclarationMethod(method, level)); + } + + public boolean hasMethod(String name, int paramCount) { + if (name.equals("constructor")) { + return hasConstructor(paramCount); + } + return methods.containsKey(name + "_" + paramCount); + } + + public boolean hasMethod(String name) { + return methods.containsKey(name); + } + + public void addStaticMethod(WordCompiler compiler, IAWord token, ClassMethodBlock method, AccessLevel level) { + // On regarde si il n'y a pas déjà une méthode du même nom + if (methods.containsKey(token.getWord())) { + compiler.addError(new AnalyzeError(token, AnalyzeErrorLevel.ERROR, Error.DUPLICATED_METHOD)); + } + if (!staticMethods.containsKey(token.getWord())) { + staticMethods.put(token.getWord(), new HashMap<>()); + staticMethodVariables.put(token.getWord(), new LeekVariable(token, VariableType.STATIC_METHOD)); + } + staticMethods.get(token.getWord()).put(method.countParameters(), new ClassDeclarationMethod(method, level)); + } + + public boolean hasStaticMethod(String name, int paramCount) { + if (staticMethods.containsKey(name) && staticMethods.get(name).containsKey(paramCount)) { + return true; + } + if (parent != null) { + return parent.hasStaticMethod(name, paramCount); + } + return false; + } + + public void addField(WordCompiler compiler, IAWord word, AbstractExpression expr, AccessLevel level) throws LeekCompilerException { + if (fields.containsKey(word.getWord()) || staticFields.containsKey(word.getWord())) { + compiler.addError(new AnalyzeError(word, AnalyzeErrorLevel.ERROR, Error.FIELD_ALREADY_EXISTS)); + return; + } + fields.put(word.getWord(), new ClassDeclarationField(expr, level)); + fieldVariables.put(word.getWord(), new LeekVariable(word, VariableType.FIELD)); + } + + public void addStaticField(IAWord word, AbstractExpression expr, AccessLevel level) throws LeekCompilerException { + if (staticFields.containsKey(word.getWord()) || fields.containsKey(word.getWord())) { + throw new LeekCompilerException(word, Error.FIELD_ALREADY_EXISTS); + } + staticFields.put(word.getWord(), new ClassDeclarationField(expr, level)); + staticFieldVariables.put(word.getWord(), new LeekVariable(word, VariableType.STATIC_FIELD)); + } + + public void declare(WordCompiler compiler) { + // On ajoute la classe + compiler.getCurrentBlock().addVariable(new LeekVariable(token, VariableType.CLASS, this)); + } + + public void analyze(WordCompiler compiler) { + compiler.setCurrentClass(this); + // Parent + if (parentToken != null) { + var parentVar = compiler.getCurrentBlock().getVariable(this.parentToken.getWord(), true); + if (parentVar == null) { + compiler.addError(new AnalyzeError(parentToken, AnalyzeErrorLevel.ERROR, Error.UNKNOWN_VARIABLE_OR_FUNCTION)); + } else if (parentVar.getVariableType() != VariableType.CLASS) { + compiler.addError(new AnalyzeError(parentToken, AnalyzeErrorLevel.ERROR, Error.UNKNOWN_VARIABLE_OR_FUNCTION)); + } else { + var current = parentVar.getClassDeclaration(); + boolean ok = true; + while (current != null) { + if (current == this) { + compiler.addError(new AnalyzeError(parentToken, AnalyzeErrorLevel.ERROR, Error.EXTENDS_LOOP)); + ok = false; + break; + } + current = current.getParent(); + } + if (ok) { + this.parent = parentVar.getClassDeclaration(); + } + } + if (parent == null) { + System.out.println("issue"); + } + } + + // Fields + for (var field : fields.entrySet()) { + if (field.getValue().expression != null) { + field.getValue().expression.analyze(compiler); + } + } + // Static fields + for (var field : staticFields.entrySet()) { + if (field.getValue().expression != null) { + field.getValue().expression.analyze(compiler); + } + } + + for (var constructor : constructors.values()) { + constructor.block.analyze(compiler); + } + // Ajout du constructeur à 0 argument par défaut en public + if (!constructors.containsKey(0)) { + constructors.put(0, new ClassDeclarationMethod(null, AccessLevel.PUBLIC)); + } + + for (var method : methods.values()) { + for (var version : method.values()) { + version.block.analyze(compiler); + } + } + for (var method : staticMethods.values()) { + for (var version : method.values()) { + version.block.analyze(compiler); + } + } + compiler.setCurrentClass(null); + } + + public void declareJava(MainLeekBlock mainblock, JavaWriter writer) { + mainblock.getWordCompiler().setCurrentClass(this); + + // Declare the class as a field of the AI + String className = "u_" + token.getWord(); + writer.addLine("private ClassLeekValue " + className + " = new ClassLeekValue(this, \"" + token.getWord() + "\");"); + + // Static methods + for (Entry> method : staticMethods.entrySet()) { + for (Entry version : method.getValue().entrySet()) { + writer.currentBlock = version.getValue().block; + String methodName = className + "_" + method.getKey() + "_" + version.getKey(); + writer.addCode("private final Object " + methodName + "("); + int i = 0; + for (var arg : version.getValue().block.getParametersDeclarations()) { + if (i++ > 0) writer.addCode(", "); + var letter = arg.isCaptured() ? "p" : "u"; + writer.addCode("Object " + letter + "_" + arg.getToken()); + } + writer.addLine(") throws LeekRunException {"); + writer.addLine("final var u_class = " + className + ";", version.getValue().block.getLine(), version.getValue().block.getFile()); + if (parent != null) { + writer.addLine("final var u_super = u_" + parent.token.getWord() + ";"); + } + for (var arg : version.getValue().block.getParametersDeclarations()) { + if (arg.isCaptured()) { + writer.addLine("final var u_" + arg.getToken() + " = new Box(" + writer.getAIThis() + ", p_" + arg.getToken() + ");"); + } + } + version.getValue().block.writeJavaCode(mainblock, writer); + writer.addLine("}"); + writer.currentBlock = null; + } + } + + // Methods + for (var method : methods.entrySet()) { + for (var version : method.getValue().entrySet()) { + writer.currentBlock = version.getValue().block; + String methodName = className + "_" + method.getKey() + "_" + version.getKey(); + writer.addCode("private final Object " + methodName + "(ObjectLeekValue u_this"); + for (var arg : version.getValue().block.getParametersDeclarations()) { + var letter = arg.isCaptured() ? "p" : "u"; + writer.addCode(", Object " + letter + "_" + arg.getToken()); + } + writer.addCode(") throws LeekRunException {"); + writer.addLine("final var u_class = " + className + ";", version.getValue().block.getLine(), version.getValue().block.getFile()); + if (parent != null) { + writer.addLine("final var u_super = u_" + parent.token.getWord() + ";"); + } + for (var arg : version.getValue().block.getParametersDeclarations()) { + if (arg.isCaptured()) { + writer.addLine("final var u_" + arg.getToken() + " = new Box(" + writer.getAIThis() + ", p_" + arg.getToken() + ");"); + } + } + writer.addCounter(1); + version.getValue().block.writeJavaCode(mainblock, writer); + writer.addLine("}"); + writer.currentBlock = null; + } + } + + // Constructeurs + for (Entry construct : constructors.entrySet()) { + writer.currentBlock = construct.getValue().block; + String methodName = className + "_" + construct.getKey(); + writer.addCode("private final Object " + methodName + "(ObjectLeekValue u_this"); + if (construct.getValue().block != null) { + for (var arg : construct.getValue().block.getParametersDeclarations()) { + var letter = arg.isCaptured() ? "p" : "u"; + writer.addCode(", Object " + letter + "_" + arg.getToken()); + } + } + writer.addCode(") throws LeekRunException {"); + writer.addLine("final var u_class = " + className + ";"); + if (parent != null) { + writer.addLine("final var u_super = u_" + parent.token.getWord() + ";"); + } + if (construct.getValue().block != null) { + for (var arg : construct.getValue().block.getParametersDeclarations()) { + if (arg.isCaptured()) { + writer.addLine("final var u_" + arg.getToken() + " = new Box(" + writer.getAIThis() + ", p_" + arg.getToken() + ");"); + } + } + construct.getValue().block.writeJavaCode(mainblock, writer); + } else { + writer.addLine("return null;"); + } + writer.addLine("}"); + writer.currentBlock = null; + } + } + + public void createJava(MainLeekBlock mainblock, JavaWriter writer) { + + mainblock.getWordCompiler().setCurrentClass(this); + + // Create the class in the constructor of the AI + String className = "u_" + token.getWord(); + + if (parent != null) { + writer.addLine(className + ".setParent(u_" + parent.getName() + ");"); + } + + writer.addCode(className + ".initFields = new LeekAnonymousFunction() {"); + writer.addLine("public Object run(ObjectLeekValue u_this, Object... values) throws LeekRunException {"); + ClassDeclarationInstruction current = this; + ArrayList classes = new ArrayList<>(); + while (current != null) { + classes.add(current); + current = current.parent; + } + for (int i = classes.size() - 1; i >= 0; --i) { + var clazz = classes.get(i); + for (var field : clazz.fields.entrySet()) { + writer.addCode("u_this.addField(" + writer.getAIThis() + ", \"" + field.getKey() + "\", "); + if (field.getValue().expression != null) { + field.getValue().expression.writeJavaCode(mainblock, writer); + } else { + writer.addCode("null"); + } + writer.addLine(", AccessLevel." + field.getValue().level + ");"); + } + } + writer.addLine("return null;"); + writer.addLine("}};"); + + writeFields(mainblock, writer, className); + + // Static methods + for (var method : staticMethods.entrySet()) { + for (var version : method.getValue().entrySet()) { + String methodName = className + "_" + method.getKey() + "_" + version.getKey(); + writer.addCode(className); + writer.addCode(".addStaticMethod(\"" + method.getKey() + "\", " + version.getKey() + ", new LeekFunction() { public Object run(Object... args) throws LeekRunException { return " + methodName + "("); + int i = 0; + for (var a = 0; a < version.getValue().block.getParameters().size(); ++a) { + if (i > 0) writer.addCode(", "); + writer.addCode("args[" + i + "]"); + i++; + } + writer.addLine("); }}, AccessLevel." + version.getValue().level + ");"); + } + writer.addCode(className); + writer.addLine(".addGenericStaticMethod(\"" + method.getKey() + "\");"); + } + + for (var construct : constructors.entrySet()) { + String methodName = className + "_" + construct.getKey(); + writer.addCode(className); + writer.addCode(".addConstructor(" + construct.getKey() + ", new LeekAnonymousFunction() { public Object run(ObjectLeekValue thiz, Object... args) throws LeekRunException { " + methodName + "(thiz"); + int i = 0; + if (construct.getValue().block != null) { + for (var a = 0; a < construct.getValue().block.getParameters().size(); ++a) { + writer.addCode(", args[" + i++ + "]"); + } + } + writer.addLine("); return thiz; }}, AccessLevel." + construct.getValue().level + ");"); + } + + for (var method : methods.entrySet()) { + for (var version : method.getValue().entrySet()) { + String methodName = className + "_" + method.getKey() + "_" + version.getKey(); + writer.addCode(className); + writer.addCode(".addMethod(\"" + method.getKey() + "\", " + version.getKey() + ", new LeekAnonymousFunction() { public Object run(ObjectLeekValue thiz, Object... args) throws LeekRunException { return " + methodName + "(thiz"); + int i = 0; + for (var a = 0; a < version.getValue().block.getParameters().size(); ++a) { + writer.addCode(", args[" + i++ + "]"); + } + writer.addLine("); }}, AccessLevel." + version.getValue().level + ");"); + } + writer.addCode(className); + writer.addLine(".addGenericMethod(\"" + method.getKey() + "\");"); + } + } + + public void initializeStaticFields(MainLeekBlock mainblock, JavaWriter writer) { + + mainblock.getWordCompiler().setCurrentClass(this); + + // Create the class in the constructor of the AI + String className = "u_" + token.getWord(); + + // First declare all static fields + for (var field : staticFields.entrySet()) { + writer.addCode(className); + writer.addCode(".addStaticField(" + writer.getAIThis() + ", \"" + field.getKey() + "\", "); + writer.addCode("null"); + writer.addCode(", AccessLevel." + field.getValue().level); + writer.addLine(");"); + } + + // Second assign values for fields with values + for (var field : staticFields.entrySet()) { + if (field.getValue().expression != null) { + writer.addCode(className); + writer.addCode(".setField(\"" + field.getKey() + "\", "); + field.getValue().expression.writeJavaCode(mainblock, writer); + writer.addLine(");"); + } + } + } + + @Override + public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { + + } + + private void writeFields(MainLeekBlock mainblock, JavaWriter writer, String className) { + + if (parent != null) { + parent.writeFields(mainblock, writer, className); + } + + for (Entry field : fields.entrySet()) { + writer.addCode(className); + writer.addCode(".addField(\"" + field.getKey() + "\""); + writer.addCode(", AccessLevel." + field.getValue().level); + writer.addLine(");"); + } + } + + public IAWord getParentToken() { + return parentToken; + } + + public ClassDeclarationInstruction getParent() { + return parent; + } + + public boolean hasMember(String field) { + return getMember(field) != null; + } + + public boolean hasStaticMember(String field) { + return getStaticMember(field) != null; + } + + public LeekVariable getMember(String token) { + var f = fieldVariables.get(token); + if (f != null) return f; + + var m = methodVariables.get(token); + if (m != null) return m; + + if (parent != null) { + return parent.getMember(token); + } + return null; + } + + public LeekVariable getStaticMember(String token) { + var f = staticFieldVariables.get(token); + if (f != null) return f; + + var m = staticMethodVariables.get(token); + if (m != null) return m; + + if (parent != null) { + return parent.getStaticMember(token); + } + return null; + } + + public HashMap getMethod(String name) { + return methods.get(name); + } + + public String getMethodName(String name, int argumentCount) { + var versions = methods.get(name); + if (versions != null) { + if (versions.containsKey(argumentCount)) return getName() + "_" + name + "_" + argumentCount; + } + if (parent != null) { + return parent.getMethodName(name, argumentCount); + } + return null; + } + + public HashMap getStaticMethod(String name) { + return staticMethods.get(name); + } + + public String getStaticMethodName(String name, int argumentCount) { + var versions = staticMethods.get(name); + if (versions != null) { + if (versions.containsKey(argumentCount)) return getName() + "_" + name + "_" + argumentCount; + } + if (parent != null) { + return parent.getStaticMethodName(name, argumentCount); + } + return null; + } + + public ClassDeclarationMethod getStaticMethod(String method, int argumentCount) { + var versions = staticMethods.get(method); + if (versions != null) { + if (versions.containsKey(argumentCount)) return versions.get(argumentCount); + } + if (parent != null) { + return parent.getStaticMethod(method, argumentCount); + } + return null; + } + + @Override + public int getOperations() { + return 0; + } + + public boolean descendsFrom(ClassDeclarationInstruction clazz) { + var current = this; + while (current != null) { + if (current == clazz) return true; + if (current.parent != null) { + current = current.parent; + } else { + return false; + } + } + return false; + } + + public boolean hasField(String field) { + return getField(field) != null; + } + + public LeekVariable getField(String token) { + var f = fieldVariables.get(token); + if (f != null) return f; + + if (parent != null) { + return parent.getField(token); + } + return null; + } +} diff --git a/src/main/java/leekscript/compiler/instruction/LeekBreakInstruction.java b/src/main/java/leekscript/compiler/instruction/LeekBreakInstruction.java index 5d1e66ad..331a0d30 100644 --- a/src/main/java/leekscript/compiler/instruction/LeekBreakInstruction.java +++ b/src/main/java/leekscript/compiler/instruction/LeekBreakInstruction.java @@ -2,6 +2,7 @@ import leekscript.compiler.AIFile; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.bloc.MainLeekBlock; public class LeekBreakInstruction implements LeekInstruction { @@ -18,7 +19,7 @@ public LeekBreakInstruction(int count, int line, AIFile ai) { @Override public String getCode() { - return "break"; + return "break;"; } @Override @@ -36,4 +37,14 @@ public int getEndBlock() { public boolean putCounterBefore() { return true; } + + @Override + public void analyze(WordCompiler compiler) { + + } + + @Override + public int getOperations() { + return 0; + } } diff --git a/src/main/java/leekscript/compiler/instruction/LeekContinueInstruction.java b/src/main/java/leekscript/compiler/instruction/LeekContinueInstruction.java index 4f8321d1..f9b785a8 100644 --- a/src/main/java/leekscript/compiler/instruction/LeekContinueInstruction.java +++ b/src/main/java/leekscript/compiler/instruction/LeekContinueInstruction.java @@ -2,6 +2,7 @@ import leekscript.compiler.AIFile; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.bloc.MainLeekBlock; public class LeekContinueInstruction implements LeekInstruction { @@ -18,7 +19,7 @@ public LeekContinueInstruction(int count, int line, AIFile ai) { @Override public String getCode() { - return "continue"; + return "continue;"; } @Override @@ -36,4 +37,14 @@ public int getEndBlock() { public boolean putCounterBefore() { return true; } + + @Override + public void analyze(WordCompiler compiler) { + + } + + @Override + public int getOperations() { + return 0; + } } diff --git a/src/main/java/leekscript/compiler/instruction/LeekExpressionInstruction.java b/src/main/java/leekscript/compiler/instruction/LeekExpressionInstruction.java index c4102adb..dad6262d 100644 --- a/src/main/java/leekscript/compiler/instruction/LeekExpressionInstruction.java +++ b/src/main/java/leekscript/compiler/instruction/LeekExpressionInstruction.java @@ -2,12 +2,20 @@ import leekscript.compiler.AIFile; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.bloc.MainLeekBlock; import leekscript.compiler.expression.AbstractExpression; +import leekscript.compiler.expression.LeekAnonymousFunction; import leekscript.compiler.expression.LeekBoolean; +import leekscript.compiler.expression.LeekExpression; import leekscript.compiler.expression.LeekNull; +import leekscript.compiler.expression.LeekNumber; +import leekscript.compiler.expression.LeekObjectAccess; +import leekscript.compiler.expression.LeekString; +import leekscript.compiler.expression.LeekTabularValue; import leekscript.compiler.expression.LeekTernaire; import leekscript.compiler.expression.LeekVariable; +import leekscript.compiler.expression.Operators; public class LeekExpressionInstruction implements LeekInstruction { @@ -23,25 +31,35 @@ public LeekExpressionInstruction(AbstractExpression expression, int line, AIFile @Override public String getCode() { - return mExpression.getString(); + return mExpression.getString() + ";"; } @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - if (mExpression.trim() instanceof LeekTernaire || mExpression.trim() instanceof LeekNull || mExpression.trim() instanceof LeekBoolean) { - writer.addCode("nothing("); - mExpression.writeJavaCode(mainblock, writer); - writer.addLine(");", mLine, mAI); + + // Simple values are not compiled + var trimmed = mExpression.trim(); + if (trimmed instanceof LeekExpression && ((LeekExpression) trimmed).getOperator() == Operators.REFERENCE) { + trimmed = ((LeekExpression) trimmed).getExpression2().trim(); + } + if (trimmed instanceof LeekNull || trimmed instanceof LeekBoolean || trimmed instanceof LeekNumber || trimmed instanceof LeekString || trimmed instanceof LeekVariable || trimmed instanceof LeekObjectAccess || trimmed instanceof LeekTabularValue || trimmed instanceof LeekAnonymousFunction) { + return; } - else { - if (mExpression instanceof LeekVariable) { - // We don't write a code like "variable;", useless and not - // authorized by Java - } else { - mExpression.writeJavaCode(mainblock, writer); - writer.addLine(";", mLine, mAI); - } + + // Wrap an expression with a function call to avoid 'error: not a statement' error + if (trimmed instanceof LeekTernaire || (trimmed instanceof LeekExpression && ((LeekExpression) trimmed).needsWrapper())) { + writer.addCode("ops("); + trimmed.writeJavaCode(mainblock, writer); + writer.addCode(", " + trimmed.getOperations() + ")"); + } else { + if (trimmed.getOperations() > 0) writer.addCode("ops("); + trimmed.writeJavaCode(mainblock, writer); + if (trimmed.getOperations() > 0) writer.addCode(", " + trimmed.getOperations() + ")"); } + // if (trimmed.getOperations() > 0) { + // writer.addCode("; ops(" + trimmed.getOperations() + ")"); + // } + writer.addLine(";", mLine, mAI); } @Override @@ -53,4 +71,14 @@ public int getEndBlock() { public boolean putCounterBefore() { return false; } + + @Override + public void analyze(WordCompiler compiler) { + mExpression.analyze(compiler); + } + + @Override + public int getOperations() { + return 0; + } } diff --git a/src/main/java/leekscript/compiler/instruction/LeekGlobalDeclarationInstruction.java b/src/main/java/leekscript/compiler/instruction/LeekGlobalDeclarationInstruction.java index 0b765b20..5f97831a 100644 --- a/src/main/java/leekscript/compiler/instruction/LeekGlobalDeclarationInstruction.java +++ b/src/main/java/leekscript/compiler/instruction/LeekGlobalDeclarationInstruction.java @@ -1,19 +1,23 @@ package leekscript.compiler.instruction; import leekscript.compiler.AIFile; +import leekscript.compiler.IAWord; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.bloc.MainLeekBlock; import leekscript.compiler.expression.AbstractExpression; +import leekscript.compiler.expression.LeekVariable; +import leekscript.compiler.expression.LeekVariable.VariableType; public class LeekGlobalDeclarationInstruction implements LeekInstruction { - private final String mName; + private final IAWord token; private AbstractExpression mValue = null; private final int mLine; private final AIFile mAI; - public LeekGlobalDeclarationInstruction(String name, int line, AIFile ai) { - mName = name; + public LeekGlobalDeclarationInstruction(IAWord token, int line, AIFile ai) { + this.token = token; mLine = line; mAI = ai; } @@ -23,24 +27,38 @@ public void setValue(AbstractExpression value) { } public String getName() { - return mName; + return token.getWord(); } @Override public String getCode() { - return mName + " = " + mValue.getString(); + if (mValue != null) { + return "global " + token.getWord() + " = " + mValue.getString() + ";"; + } else { + return "global " + token.getWord() + ";"; + } } @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - writer.addCode("if(globale_" + mName + " == null){globale_" + mName + " = new VariableLeekValue(mUAI, "); - if(mValue != null) mValue.writeJavaCode(mainblock, writer); - else writer.addCode("LeekValueManager.NULL"); - writer.addLine(");}", mLine, mAI); - } - - public String getJavaDeclaration() { - return "private VariableLeekValue globale_" + mName + " = null;"; + writer.addCode("if (!g_init_" + token.getWord() + ") { "); + if (mainblock.getWordCompiler().getVersion() >= 2) { + writer.addCode("g_" + token.getWord() + " = "); + if (mValue != null) { + if (mValue.getOperations() > 0) writer.addCode("ops("); + mValue.writeJavaCode(mainblock, writer); + if (mValue.getOperations() > 0) writer.addCode(", " + mValue.getOperations() + ")"); + } + else writer.addCode("null"); + } else { + writer.addCode("g_" + token.getWord() + " = new Box(" + writer.getAIThis() + ", "); + if (mValue != null) mValue.compileL(mainblock, writer); + else writer.addCode("null"); + writer.addCode(", " + (mValue != null ? mValue.getOperations() : 0) + ")"); + } + writer.addCode("; g_init_" + token.getWord() + " = true;"); + if (mainblock.getWordCompiler().getVersion() >= 2) writer.addCode(" ops(1);"); + writer.addLine(" }", mLine, mAI); } @Override @@ -52,4 +70,21 @@ public int getEndBlock() { public boolean putCounterBefore() { return false; } + + public void declare(WordCompiler compiler) { + // On ajoute la variable + compiler.getCurrentBlock().addVariable(new LeekVariable(compiler, token, VariableType.GLOBAL)); + } + + @Override + public void analyze(WordCompiler compiler) { + if (mValue != null) { + mValue.analyze(compiler); + } + } + + @Override + public int getOperations() { + return 0; + } } diff --git a/src/main/java/leekscript/compiler/instruction/LeekInstruction.java b/src/main/java/leekscript/compiler/instruction/LeekInstruction.java index 41e5c67b..d3560255 100644 --- a/src/main/java/leekscript/compiler/instruction/LeekInstruction.java +++ b/src/main/java/leekscript/compiler/instruction/LeekInstruction.java @@ -1,6 +1,7 @@ package leekscript.compiler.instruction; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.bloc.MainLeekBlock; public interface LeekInstruction { @@ -12,4 +13,8 @@ public interface LeekInstruction { public int getEndBlock(); public boolean putCounterBefore(); + + public void analyze(WordCompiler compiler); + + public int getOperations(); } diff --git a/src/main/java/leekscript/compiler/instruction/LeekReturnInstruction.java b/src/main/java/leekscript/compiler/instruction/LeekReturnInstruction.java index 94aed65d..e1730a63 100644 --- a/src/main/java/leekscript/compiler/instruction/LeekReturnInstruction.java +++ b/src/main/java/leekscript/compiler/instruction/LeekReturnInstruction.java @@ -2,6 +2,7 @@ import leekscript.compiler.AIFile; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; import leekscript.compiler.bloc.MainLeekBlock; import leekscript.compiler.expression.AbstractExpression; @@ -19,16 +20,23 @@ public LeekReturnInstruction(int count, AbstractExpression exp, int line, AIFile @Override public String getCode() { - return "return " + (mExpression == null ? "null" : mExpression.getString()); + return "return " + (mExpression == null ? "null" : mExpression.getString()) + ";"; } @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { writer.addCode("return "); - if (mExpression == null) - writer.addCode("LeekValueManager.NULL;"); - else { - mExpression.writeJavaCode(mainblock, writer); + if (mExpression == null) { + writer.addCode("null;"); + } else { + if (mExpression.getOperations() > 0) writer.addCode("ops("); + var finalExpression = mExpression.trim(); + if (mainblock.getWordCompiler().getVersion() == 1) { + finalExpression.compileL(mainblock, writer); + } else { + finalExpression.writeJavaCode(mainblock, writer); + } + if (finalExpression.getOperations() > 0) writer.addCode(", " + finalExpression.getOperations() + ")"); writer.addLine(";", mLine, mAI); } } @@ -42,4 +50,16 @@ public int getEndBlock() { public boolean putCounterBefore() { return true; } + + @Override + public void analyze(WordCompiler compiler) { + if (mExpression != null) { + mExpression.analyze(compiler); + } + } + + @Override + public int getOperations() { + return 0; + } } diff --git a/src/main/java/leekscript/compiler/instruction/LeekVariableDeclarationInstruction.java b/src/main/java/leekscript/compiler/instruction/LeekVariableDeclarationInstruction.java index b5bb2384..69263e03 100644 --- a/src/main/java/leekscript/compiler/instruction/LeekVariableDeclarationInstruction.java +++ b/src/main/java/leekscript/compiler/instruction/LeekVariableDeclarationInstruction.java @@ -1,26 +1,38 @@ package leekscript.compiler.instruction; import leekscript.compiler.AIFile; +import leekscript.compiler.AnalyzeError; +import leekscript.compiler.IAWord; import leekscript.compiler.JavaWriter; +import leekscript.compiler.WordCompiler; +import leekscript.compiler.AnalyzeError.AnalyzeErrorLevel; +import leekscript.compiler.bloc.AbstractLeekBlock; import leekscript.compiler.bloc.MainLeekBlock; import leekscript.compiler.expression.AbstractExpression; +import leekscript.compiler.expression.LeekAnonymousFunction; +import leekscript.compiler.expression.LeekExpression; +import leekscript.compiler.expression.LeekVariable; +import leekscript.compiler.expression.Operators; +import leekscript.compiler.expression.LeekVariable.VariableType; +import leekscript.common.Error; +import leekscript.common.Type; public class LeekVariableDeclarationInstruction implements LeekInstruction { - private final String mName; + private final IAWord token; private final int mLine; private final AIFile mAI; private AbstractExpression mValue = null; - private boolean mMustSepare = false; + private boolean captured = false; + private AbstractLeekBlock function; + private boolean box = false; - public LeekVariableDeclarationInstruction(String name, int line, AIFile ai) { - mName = name; + public LeekVariableDeclarationInstruction(WordCompiler compiler, IAWord token, int line, AIFile ai, AbstractLeekBlock function) { + this.token = token; mLine = line; mAI = ai; - } - - public void mustSepare() { - mMustSepare = true; + this.function = function; + this.box = compiler.getVersion() <= 1; } public void setValue(AbstractExpression value) { @@ -28,28 +40,114 @@ public void setValue(AbstractExpression value) { } public String getName() { - return mName; + return token.getWord(); + } + + public IAWord getToken() { + return this.token; + } + + public boolean isBox() { + return this.box || this.captured; + } + + public boolean isWrapper() { + return mAI.getVersion() == 1 && this.captured; } @Override public String getCode() { - if(mValue == null) return "var " + mName; - return "var " + mName + " = " + mValue.getString(); + if (mValue == null) return "var " + token.getWord(); + return "var " + token.getWord() + " = " + mValue.getString() + ";"; } @Override public void writeJavaCode(MainLeekBlock mainblock, JavaWriter writer) { - if(!mMustSepare){ - writer.addCode("final VariableLeekValue user_" + mName + " = new VariableLeekValue(mUAI, "); - if(mValue != null) mValue.writeJavaCode(mainblock, writer); - else writer.addCode("LeekValueManager.NULL"); - writer.addLine(");", mLine, mAI); - } - else{ - writer.addCode("final VariableLeekValue user_" + mName + " = new VariableLeekValue(mUAI, LeekValueManager.NULL); user_" + mName + ".set(mUAI, "); - if(mValue != null) mValue.writeJavaCode(mainblock, writer); - else writer.addCode("LeekValueManager.NULL"); - writer.addLine(");", mLine, mAI); + if (this.captured) { + if (mValue != null && mValue.trim() instanceof LeekAnonymousFunction) { + writer.addCode("final Wrapper u_" + token.getWord() + " = new Wrapper(new Box(" + writer.getAIThis() + ", null)); u_" + token.getWord() + ".set("); + mValue.writeJavaCode(mainblock, writer); + writer.addLine(");", mLine, mAI); + } else if (mValue instanceof LeekExpression && ((LeekExpression) mValue).getOperator() == Operators.REFERENCE) { + var e = ((LeekExpression) mValue).getExpression2(); + if (e.isLeftValue()) { + writer.addCode("final Wrapper u_" + token.getWord() + " = new Wrapper("); + e.compileL(mainblock, writer); + writer.addLine(", " + e.getOperations() + ")"); + } else { + writer.addCode("final var u_" + token.getWord() + " = new Wrapper(new Box(" + writer.getAIThis() + ", "); + e.writeJavaCode(mainblock, writer); + writer.addLine("), " + e.getOperations() + ")"); + } + writer.addLine(";", mLine, mAI); + } else if (mainblock.getWordCompiler().getVersion() <= 1) { + writer.addCode("final var u_" + token.getWord() + " = new Wrapper(new Box(" + writer.getAIThis() + ", "); + if (mValue != null) mValue.compileL(mainblock, writer); + else writer.addCode("null"); + writer.addLine(")"); + if (mValue != null && mValue.getOperations() > 0) { + writer.addCode(", " + mValue.getOperations()); + } + writer.addLine(");", mLine, mAI); + } else { + writer.addCode("final var u_" + token.getWord() + " = new Wrapper(new Box(" + writer.getAIThis() + ", "); + if (mValue != null) mValue.writeJavaCode(mainblock, writer); + else writer.addCode("null"); + writer.addLine(")"); + if (mValue != null && mValue.getOperations() > 0) { + writer.addCode(", " + mValue.getOperations()); + } + writer.addLine(");", mLine, mAI); + } + } else { + if (mainblock.getWordCompiler().getVersion() <= 1) { + if (mValue instanceof LeekExpression && ((LeekExpression) mValue).getOperator() == Operators.REFERENCE) { + var e = ((LeekExpression) mValue).getExpression2(); + if (e.isLeftValue()) { + // writer.addCode("Box u_" + token.getWord() + " = "); + // e.compileL(mainblock, writer); + writer.addCode("var u_" + token.getWord() + " = new Box(" + writer.getAIThis() + ", "); + // e.compileL(mainblock, writer); + e.writeJavaCode(mainblock, writer); + if (mValue.getOperations() > 0) { + writer.addCode(", " + mValue.getOperations()); + } + writer.addLine(")"); + } else { + writer.addCode("var u_" + token.getWord() + " = new Box(" + writer.getAIThis() + ", "); + // e.writeJavaCode(mainblock, writer); + mValue.compileL(mainblock, writer); + if (mValue.getOperations() > 0) { + writer.addCode(", " + mValue.getOperations()); + } + writer.addLine(")"); + } + writer.addLine(";", mLine, mAI); + } else { + writer.addCode("var u_" + token.getWord() + " = new Box(" + writer.getAIThis() + ", "); + if (mValue != null) { + if (mValue.isLeftValue()) { + writer.compileClone(mainblock, mValue); + } else { + mValue.compileL(mainblock, writer); + // mValue.writeJavaCode(mainblock, writer); + } + if (mValue.getOperations() > 0) { + writer.addCode(", " + mValue.getOperations()); + } + } else { + writer.addCode("null"); + } + writer.addLine(");", mLine, mAI); + } + } else { + writer.addCode("Object u_" + token.getWord() + " = "); + writer.addCode("ops("); + if (mValue != null) mValue.writeJavaCode(mainblock, writer); + else writer.addCode("null"); + writer.addCode(", " + (1 + (mValue == null ? 0 : mValue.getOperations())) + ")"); + writer.addLine(";", mLine, mAI); + } } } @@ -58,9 +156,56 @@ public int getEndBlock() { return 0; } + public AbstractLeekBlock getFunction() { + return this.function; + } + + public boolean isCaptured() { + return captured; + } + @Override public boolean putCounterBefore() { return false; } + @Override + public void analyze(WordCompiler compiler) { + function = compiler.getCurrentFunction(); + if (mValue != null && mValue.getType() == Type.FUNCTION) { + registerVariable(compiler); + mValue.analyze(compiler); + } else { + if (mValue != null) mValue.analyze(compiler); + registerVariable(compiler); + } + } + + private void registerVariable(WordCompiler compiler) { + // Variables interdites + if (compiler.getVersion() >= 2 && token.getWord().equals("this")) { + compiler.addError(new AnalyzeError(token, AnalyzeErrorLevel.ERROR, Error.THIS_NOT_ALLOWED_HERE)); + } else { + // Vérification déjà existante (on vérifie les globales et fonctions seulement en 1.1 car il y a un léger bug en 1.0 avec les includes) + if ((compiler.getVersion() >= 2 && (compiler.getMainBlock().hasGlobal(token.getWord()) || compiler.getMainBlock().hasUserFunction(token.getWord(), true))) || compiler.getCurrentBlock().hasVariable(token.getWord())) { + compiler.addError(new AnalyzeError(token, AnalyzeErrorLevel.ERROR, Error.VARIABLE_NAME_UNAVAILABLE)); + } else { + // On ajoute la variable + compiler.getCurrentBlock().addVariable(new LeekVariable(token, VariableType.LOCAL, this)); + } + } + } + + public void setCaptured() { + this.captured = true; + } + + public void setFunction(AbstractLeekBlock function) { + this.function = function; + } + + @Override + public int getOperations() { + return 0; + } } diff --git a/src/main/java/leekscript/compiler/resolver/FileSystemContext.java b/src/main/java/leekscript/compiler/resolver/FileSystemContext.java index bb63ed56..a0d2b2ec 100644 --- a/src/main/java/leekscript/compiler/resolver/FileSystemContext.java +++ b/src/main/java/leekscript/compiler/resolver/FileSystemContext.java @@ -2,19 +2,21 @@ import java.io.File; +import com.alibaba.fastjson.JSONObject; + public class FileSystemContext extends ResolverContext { - + private File folder; - + public FileSystemContext(File folder) { this.folder = folder; } - + @Override public String toString() { return folder.getPath(); } - + public File getFolder() { return folder; } @@ -22,4 +24,9 @@ public File getFolder() { public void setFolder(File folder) { this.folder = folder; } + + @Override + public void toJson(JSONObject json) { + + } } diff --git a/src/main/java/leekscript/compiler/resolver/FileSystemResolver.java b/src/main/java/leekscript/compiler/resolver/FileSystemResolver.java index 5060fa94..a4574d42 100644 --- a/src/main/java/leekscript/compiler/resolver/FileSystemResolver.java +++ b/src/main/java/leekscript/compiler/resolver/FileSystemResolver.java @@ -1,8 +1,8 @@ package leekscript.compiler.resolver; -import java.io.IOException; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -12,22 +12,34 @@ public class FileSystemResolver implements Resolver { @Override - public AIFile resolve(String path, ResolverContext basecontext) { - + public AIFile resolve(String path, ResolverContext basecontext) throws FileNotFoundException { + FileSystemContext context = (FileSystemContext) basecontext; if (context == null) { context = new FileSystemContext(Paths.get(".").toFile()); } try { Path resolvedPath = context.getFolder().toPath().resolve(path).normalize(); - - String code = new String(Files.readAllBytes(resolvedPath), StandardCharsets.UTF_8); - - FileSystemContext newContext = new FileSystemContext(resolvedPath.getParent().toFile()); - return new AIFile(path, code, newContext); - - } catch (IOException e) { - return null; + + var is = new FileInputStream(resolvedPath.toFile()); + String code = new String(is.readAllBytes(), StandardCharsets.UTF_8); + + Path parent = resolvedPath.getParent(); + if (parent == null) parent = Paths.get("."); + + FileSystemContext newContext = new FileSystemContext(parent.toFile()); + + long timestamp = resolvedPath.toFile().lastModified(); + int version = resolvedPath.toFile().getName().contains("v1") ? 1 : 2; + + return new AIFile(path, code, timestamp, version, newContext, resolvedPath.toString().hashCode() & 0xfffffff); + + } catch (Exception e) { + throw new FileNotFoundException(); } } + + public ResolverContext createContext(int parameter1, int parameter2, int parameter3) { + return new FileSystemContext(Paths.get(".").toFile()); + } } diff --git a/src/main/java/leekscript/compiler/resolver/Resolver.java b/src/main/java/leekscript/compiler/resolver/Resolver.java index 41af7612..9dc07d39 100644 --- a/src/main/java/leekscript/compiler/resolver/Resolver.java +++ b/src/main/java/leekscript/compiler/resolver/Resolver.java @@ -1,9 +1,11 @@ package leekscript.compiler.resolver; +import java.io.FileNotFoundException; + import leekscript.compiler.AIFile; public interface Resolver { - + public class Result { public final String code; public final C context; @@ -12,12 +14,15 @@ public Result(String code, C context) { this.context = context; } } - + /** * Resolve a AI by path, like 'library.leek' or '../../test.leek' * @param path AI path * @param context The AI's context or null to initialize it from default - * @return The result with the AI's code and context, or null if not found + * @return The result with the AI's code and context + * @throws FileNotFoundException if the AI is not found */ - abstract public AIFile resolve(String path, ResolverContext context); + abstract public AIFile resolve(String path, ResolverContext context) throws FileNotFoundException; + + public abstract ResolverContext createContext(int parameter1, int parameter2, int parameter3); } diff --git a/src/main/java/leekscript/compiler/resolver/ResolverContext.java b/src/main/java/leekscript/compiler/resolver/ResolverContext.java index be5c6baa..a27e2b5d 100644 --- a/src/main/java/leekscript/compiler/resolver/ResolverContext.java +++ b/src/main/java/leekscript/compiler/resolver/ResolverContext.java @@ -1,5 +1,8 @@ package leekscript.compiler.resolver; +import com.alibaba.fastjson.JSONObject; + public abstract class ResolverContext { + public abstract void toJson(JSONObject json); } diff --git a/src/main/java/leekscript/compiler/resolver/ResourceContext.java b/src/main/java/leekscript/compiler/resolver/ResourceContext.java new file mode 100644 index 00000000..8ab1c27e --- /dev/null +++ b/src/main/java/leekscript/compiler/resolver/ResourceContext.java @@ -0,0 +1,32 @@ +package leekscript.compiler.resolver; + +import java.io.File; + +import com.alibaba.fastjson.JSONObject; + +public class ResourceContext extends ResolverContext { + + private File folder; + + public ResourceContext(File folder) { + this.folder = folder; + } + + @Override + public String toString() { + return folder.getPath(); + } + + public File getFolder() { + return folder; + } + + public void setFolder(File folder) { + this.folder = folder; + } + + @Override + public void toJson(JSONObject json) { + + } +} diff --git a/src/main/java/leekscript/compiler/resolver/ResourceResolver.java b/src/main/java/leekscript/compiler/resolver/ResourceResolver.java new file mode 100644 index 00000000..430fd0af --- /dev/null +++ b/src/main/java/leekscript/compiler/resolver/ResourceResolver.java @@ -0,0 +1,43 @@ +package leekscript.compiler.resolver; + +import java.io.FileNotFoundException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; + +import leekscript.compiler.AIFile; + + +public class ResourceResolver implements Resolver { + + @Override + public AIFile resolve(String path, ResolverContext basecontext) throws FileNotFoundException { + + ResourceContext context = (ResourceContext) basecontext; + if (context == null) { + context = new ResourceContext(Paths.get(".").toFile()); + } + try { + Path resolvedPath = context.getFolder().toPath().resolve(path).normalize(); + + var is = getClass().getClassLoader().getResourceAsStream(resolvedPath.toString()); + String code = new String(is.readAllBytes(), StandardCharsets.UTF_8); + + Path parent = resolvedPath.getParent(); + if (parent == null) parent = Paths.get("."); + + var newContext = new ResourceContext(parent.toFile()); + + long timestamp = resolvedPath.toFile().lastModified(); + + return new AIFile(path, code, timestamp, 2, newContext, resolvedPath.toString().hashCode() & 0xfffffff); + + } catch (Exception e) { + throw new FileNotFoundException(); + } + } + + public ResolverContext createContext(int parameter1, int parameter2, int parameter3) { + return new FileSystemContext(Paths.get(".").toFile()); + } +} diff --git a/src/main/java/leekscript/functions/Function.java b/src/main/java/leekscript/functions/Function.java deleted file mode 100644 index aee31092..00000000 --- a/src/main/java/leekscript/functions/Function.java +++ /dev/null @@ -1,30 +0,0 @@ -package leekscript.functions; - -public class Function { - - private final String mName; - private final int mArgumentCount; - private final int mOperations; - - public Function(String name, int argumentcount, String operations) { - mName = name; - mArgumentCount = argumentcount; - int nb = -1; - try { - nb = Integer.parseInt(operations); - } catch (Exception e) {} - mOperations = nb; - } - - public String getName() { - return mName; - } - - public int countArguments() { - return mArgumentCount; - } - - public int getOperations() { - return mOperations; - } -} diff --git a/src/main/java/leekscript/functions/Functions.java b/src/main/java/leekscript/functions/Functions.java index cd1301f8..226ceb66 100644 --- a/src/main/java/leekscript/functions/Functions.java +++ b/src/main/java/leekscript/functions/Functions.java @@ -1,53 +1,28 @@ package leekscript.functions; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.TreeMap; import com.alibaba.fastjson.JSONObject; +import leekscript.runner.ILeekFunction; +import leekscript.runner.LeekFunctions; + public class Functions { - private static boolean sReady = false; - private static List sFunctions = new ArrayList(); private static Map sVariableOperations = new TreeMap(); - public static void addFunction(Function function) { - sFunctions.add(function); - } - - public static void addVariableOperations(String name, String variableOperations) { - sVariableOperations.put(name, new VariableOperations(JSONObject.parseObject(variableOperations))); + public static void addFunctionOperations(String function, int operations, String variableOperations) { + ILeekFunction f = LeekFunctions.getValue(function); + if (f != null) { + f.setOperations(Math.max(1, operations)); + } + if (variableOperations != null) { + sVariableOperations.put(function, new VariableOperations(JSONObject.parseObject(variableOperations))); + } } public static VariableOperations getVariableOperations(String name) { return sVariableOperations.get(name); } - - public static boolean isReady() { - - return sReady; - } - - public static void setReady(boolean ready) { - sReady = ready; - } - - public static Function getFunction(String name, int params) { - for (Function f : sFunctions) { - if (f.getName().equals(name) && f.countArguments() == params) - return f; - } - return null; - } - - public static Integer getOperations(String name) { - for (Function f : sFunctions) { - if (f.getName().equals(name)) { - return f.getOperations(); - } - } - return 1; - } } diff --git a/src/main/java/leekscript/runner/AI.java b/src/main/java/leekscript/runner/AI.java index 35a26eec..325ce647 100644 --- a/src/main/java/leekscript/runner/AI.java +++ b/src/main/java/leekscript/runner/AI.java @@ -1,245 +1,645 @@ package leekscript.runner; import leekscript.AILog; +import leekscript.compiler.LeekScript; +import leekscript.compiler.LineMapping; +import leekscript.compiler.RandomGenerator; import leekscript.runner.PhpArray.Element; -import leekscript.runner.values.AbstractLeekValue; import leekscript.runner.values.ArrayLeekValue; -import leekscript.runner.values.NullLeekValue; -import leekscript.runner.values.StringLeekValue; -import leekscript.runner.values.VariableLeekValue; +import leekscript.runner.values.ClassLeekValue; +import leekscript.runner.values.FunctionLeekValue; +import leekscript.runner.values.LeekValue; +import leekscript.runner.values.ObjectLeekValue; +import leekscript.runner.values.Box; import leekscript.runner.values.ArrayLeekValue.ArrayIterator; +import leekscript.common.Error; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; import java.util.Comparator; -import java.util.Iterator; +import java.util.HashSet; +import java.util.Random; +import java.util.Set; +import java.util.TreeMap; +import java.util.Map; +import java.util.HashMap; +import java.util.Collections; +import java.util.List; +import java.util.ArrayList; +import java.util.stream.Stream; import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONArray; public abstract class AI { - - public static final int ERROR_LOG_COST = 1000; + + public static final int DOUBLE = 1; + public static final int INT = 2; + public static final int BOOLEAN = 3; + public static final int STRING = 4; + public static final int NULL = 5; + public static final int ARRAY = 6; + public static final int NUMBER = 7; + public static final int FUNCTION = 8; + + public static final int ERROR_LOG_COST = 10000; public final static int MAX_MEMORY = 100000; - protected long mOperations = 0; - public final static long MAX_OPERATIONS = 20000000; + protected int mOperations = 0; + public final static int MAX_OPERATIONS = 20000000; + public int maxOperations = MAX_OPERATIONS; - protected JSONArray mErrorObject = null; + protected TreeMap mLinesMapping = new TreeMap<>(); protected String thisObject = null; + protected int id; + protected int version; protected AILog logs; - protected AI mUAI; + // protected AI mUAI; + protected int mInstructions; + protected RandomGenerator randomGenerator; + private long analyzeTime; + private long compileTime; + private long loadTime; + private File filesLines; + + public final ClassLeekValue valueClass; + public final ClassLeekValue nullClass; + public final ClassLeekValue booleanClass; + public final ClassLeekValue integerClass; + public final ClassLeekValue realClass; + public final ClassLeekValue numberClass; + public final ClassLeekValue arrayClass; + public final ClassLeekValue stringClass; + public final ClassLeekValue objectClass; + public final ClassLeekValue functionClass; + public final ClassLeekValue classClass; + public final ClassLeekValue jsonClass; + public final ClassLeekValue systemClass; + + private final Map callstackOperations = new HashMap(); + private boolean doProfile = false; - public AI() { - mUAI = this; + public AI(int instructions, int version) { + this.mInstructions = instructions; + this.version = version; logs = new AILog(); + + randomGenerator = new RandomGenerator() { + private Random random = new Random(); + + @Override + public void seed(long seed) { + random.setSeed(seed); + } + + @Override + public int getInt(int min, int max) { + if (max - min + 1 <= 0) + return 0; + return min + random.nextInt(max - min + 1); + } + + @Override + public double getDouble() { + return random.nextDouble(); + } + }; + + valueClass = new ClassLeekValue(this, "Value"); + nullClass = new ClassLeekValue(this, "Null", valueClass); + booleanClass = new ClassLeekValue(this, "Boolean", valueClass); + numberClass = new ClassLeekValue(this, "Number", valueClass); + realClass = new ClassLeekValue(this, "Real", numberClass); + integerClass = new ClassLeekValue(this, "Integer", realClass); + arrayClass = new ClassLeekValue(this, "Array", valueClass); + stringClass = new ClassLeekValue(this, "String", valueClass); + objectClass = new ClassLeekValue(this, "Object", valueClass); + functionClass = new ClassLeekValue(this, "Function", valueClass); + classClass = new ClassLeekValue(this, "Class", valueClass); + jsonClass = new ClassLeekValue(this, "JSON"); + systemClass = new ClassLeekValue(this, "System"); + + try { + init(); + } catch (Exception e) {} + } + + public void setId(int id) { + this.id = id; } - public long getOperations() { + public int getId() { + return id; + } + + // Method that can be overriden in each AI + protected void init() throws Exception {} + + // Method that can be overriden in each AI + public void staticInit() throws Exception {} + + public int getInstructions() { + return mInstructions; + } + + public int operations() { + return mOperations; + } + + public int getOperations() throws LeekRunException { return mOperations; } - + + public boolean isProfileEnabled() { + return doProfile; + } + + public void enableProfile(boolean p) { + doProfile = p; + } + public AILog getLogs() { return logs; } - public void addOperations(int nb) throws LeekRunException { + public void ops(int nb) throws LeekRunException { + // System.out.println("ops " + nb); mOperations += nb; - if (mOperations >= MAX_OPERATIONS) { + if (mOperations >= maxOperations) { throw new LeekRunException(LeekRunException.TOO_MUCH_OPERATIONS); } } + public Object ops(Object x, int nb) throws LeekRunException { + ops(nb); + return x; + } + + public int ops(int x, int nb) throws LeekRunException { + ops(nb); + return x; + } + + public double ops(double x, int nb) throws LeekRunException { + ops(nb); + return x; + } + + public boolean ops(boolean x, int nb) throws LeekRunException { + ops(nb); + return x; + } + + public void addOperationsNoCheck(int nb) { + // System.out.println("ops " + nb); + mOperations += nb; + } + public void resetCounter() { mOperations = 0; } - protected NullLeekValue nothing(AbstractLeekValue obj) throws Exception { - return LeekValueManager.NULL; + protected Object nothing(Object obj) throws LeekRunException { + return null; } public String getErrorMessage(StackTraceElement[] elements) { + StringBuilder sb = new StringBuilder(); + int count = 0; for (StackTraceElement element : elements) { - if (element.getMethodName().equals("runIA") || element.getMethodName().startsWith("user_function_")) { - return getErrorLocalisation(element.getLineNumber()); + // System.out.println(element.getClassName() + " " + element.getMethodName() + " " + element.getLineNumber()); + if (element.getClassName().startsWith("AI_")) { + sb.append(getErrorLocalisation(element.getLineNumber())); + if (count++ > 50) { + sb.append("[...]"); + break; + } } } - return ""; + // for (StackTraceElement element : elements) { + // sb.append("\t▶ " + element.getClassName() + "." + element.getMethodName() + ", line " + element.getLineNumber()).append("\n"); + // } + return sb.toString(); } public String getErrorMessage(Throwable e) { return getErrorMessage(e.getStackTrace()); } - protected String getErrorLocalisation(int line) { - if (mErrorObject == null) { - mErrorObject = JSONArray.parseArray(getErrorString()); - thisObject = getAItring(); + protected String getErrorLocalisation(int javaLine) { + if (mLinesMapping.isEmpty() && this.filesLines != null && this.filesLines.exists()) { + try (Stream stream = Files.lines(this.filesLines.toPath())) { + stream.forEach(l -> { + var parts = l.split(" "); + mLinesMapping.put(Integer.parseInt(parts[0]), new LineMapping(Integer.parseInt(parts[2]), Integer.parseInt(parts[1]))); + }); + } catch (IOException e) {} + thisObject = getAIString(); } - int value = 0; - for (int i = 0; i < mErrorObject.size(); i++) { - if (mErrorObject.getJSONArray(i).getInteger(0) > line) { - break; - } - value = i; + var lineMapping = mLinesMapping.get(javaLine); + if (lineMapping != null) { + var files = getErrorFiles(); + var f = lineMapping.getAI(); + String file = f < files.length ? files[f] : "?"; + return "\t▶ AI " + file + ", line " + lineMapping.getLeekScriptLine() + "\n"; // + ", java " + line; } - if (mErrorObject.size() > value) { - JSONArray l = mErrorObject.getJSONArray(value); - if (l != null && l.size() >= 3) { - String ai_name = thisObject != null ? thisObject : ""; - return "(IA : " + ai_name + ", line : " + l.getString(2) + ")"; + return ""; + } + + public int abs(int x) throws LeekRunException { + ops(LeekFunctions.abs.getOperations()); + return Math.abs(x); + } + + public double abs(Number x) throws LeekRunException { + ops(LeekFunctions.abs.getOperations()); + return Math.abs(x.doubleValue()); + } + + public Object abs(Object... args) throws LeekRunException { + if (check("abs", new int[] { NUMBER }, args)) { + ops(LeekFunctions.abs.getOperations()); + if (args[0] instanceof Integer) { + return Math.abs(((Integer) args[0]).intValue()); } + return Math.abs(((Number) args[0]).doubleValue()); } - return ""; + return 0; + } + + public int ceil(int x) throws LeekRunException { + ops(LeekFunctions.ceil.getOperations()); + return x; + } + + public int ceil(double x) throws LeekRunException { + ops(LeekFunctions.ceil.getOperations()); + return (int) Math.ceil(x); + } + + public int ceil(Object... args) throws LeekRunException { + if (check("ceil", new int[] { NUMBER }, args)) { + ops(LeekFunctions.ceil.getOperations()); + return (int) Math.ceil(((Number) args[0]).doubleValue()); + } + return 0; + } + + public int floor(Number x) throws LeekRunException { + ops(LeekFunctions.floor.getOperations()); + return (int) Math.floor(x.doubleValue()); + } + + public int floor(double x) throws LeekRunException { + ops(LeekFunctions.floor.getOperations()); + return (int) Math.floor(x); + } + + public int floor(Object... args) throws LeekRunException { + if (check("floor", new int[] { NUMBER }, args)) { + ops(LeekFunctions.floor.getOperations()); + return (int) Math.floor(((Number) args[0]).doubleValue()); + } + return 0; + } + + public int round(int x) throws LeekRunException { + ops(LeekFunctions.round.getOperations()); + return x; + } + + public int round(double x) throws LeekRunException { + ops(LeekFunctions.round.getOperations()); + return (int) Math.round(x); + } + + public int round(Object... args) throws LeekRunException { + if (check("round", new int[] { NUMBER }, args)) { + ops(LeekFunctions.round.getOperations()); + return (int) Math.round(((Number) args[0]).doubleValue()); + } + return 0; + } + + public double cos(double x) throws LeekRunException { + ops(LeekFunctions.cos.getOperations()); + return Math.cos(x); + } + + public double cos(Object... args) throws LeekRunException { + if (check("cos", new int[] { NUMBER }, args)) { + ops(LeekFunctions.cos.getOperations()); + return Math.cos(((Number) args[0]).doubleValue()); + } + return 0; + } + + public double acos(double x) throws LeekRunException { + ops(LeekFunctions.acos.getOperations()); + return Math.acos(x); + } + + public double acos(Object... args) throws LeekRunException { + if (check("acos", new int[] { NUMBER }, args)) { + ops(LeekFunctions.acos.getOperations()); + return Math.acos(((Number) args[0]).doubleValue()); + } + return 0; + } + + public double sin(double x) throws LeekRunException { + ops(LeekFunctions.sin.getOperations()); + return Math.sin(x); + } + + public double sin(Object... args) throws LeekRunException { + if (check("sin", new int[] { NUMBER }, args)) { + ops(LeekFunctions.sin.getOperations()); + return Math.sin(((Number) args[0]).doubleValue()); + } + return 0; + } + + public double asin(double x) throws LeekRunException { + ops(LeekFunctions.asin.getOperations()); + return Math.asin(x); + } + + public double asin(Object... args) throws LeekRunException { + if (check("asin", new int[] { NUMBER }, args)) { + ops(LeekFunctions.asin.getOperations()); + return Math.asin(((Number) args[0]).doubleValue()); + } + return 0; + } + + public double tan(double x) throws LeekRunException { + ops(LeekFunctions.tan.getOperations()); + return Math.tan(x); } - + public double tan(Object... args) throws LeekRunException { + if (check("tan", new int[] { NUMBER }, args)) { + ops(LeekFunctions.tan.getOperations()); + return Math.tan(((Number) args[0]).doubleValue()); + } + return 0; + } - public AbstractLeekValue color(AbstractLeekValue red, AbstractLeekValue green, AbstractLeekValue blue) throws LeekRunException { - return LeekValueManager.getLeekIntValue(((red.getInt(this) & 255) << 16) | ((green.getInt(this) & 255) << 8) | (blue.getInt(this) & 255)); + public double atan(double x) throws LeekRunException { + ops(LeekFunctions.atan.getOperations()); + return Math.atan(x); } - public int typeOf(AbstractLeekValue value) { - if (value.getType() == AbstractLeekValue.ARRAY) - return (int) LeekConstants.TYPE_ARRAY.getValue(); - if (value.getType() == AbstractLeekValue.NULL) - return (int) LeekConstants.TYPE_NULL.getValue(); - if (value.getType() == AbstractLeekValue.STRING) - return (int) LeekConstants.TYPE_STRING.getValue(); - if (value.getType() == AbstractLeekValue.NUMBER) - return (int) LeekConstants.TYPE_NUMBER.getValue(); - if (value.getType() == AbstractLeekValue.BOOLEAN) - return (int) LeekConstants.TYPE_BOOLEAN.getValue(); - if (value.getType() == AbstractLeekValue.FUNCTION) - return (int) LeekConstants.TYPE_FUNCTION.getValue(); + public double atan(Object... args) throws LeekRunException { + if (check("atan", new int[] { NUMBER }, args)) { + ops(LeekFunctions.atan.getOperations()); + return Math.atan(((Number) args[0]).doubleValue()); + } return 0; } - public void arrayFlatten(ArrayLeekValue array, ArrayLeekValue retour, int depth) throws Exception { - for (AbstractLeekValue value : array) { + public int count(ArrayLeekValue array) throws LeekRunException { + ops(LeekFunctions.count.getOperations()); + return array.size(); + } + + public Object count(Object... args) throws LeekRunException { + if (check("count", new int[] { ARRAY }, args)) { + ops(LeekFunctions.count.getOperations()); + var array = (ArrayLeekValue) args[0]; + return array.size(); + } + return null; + } + + public Object debug(Object x) throws LeekRunException { + String p = LeekValueManager.getString(this, x); + getLogs().addLog(AILog.STANDARD, p); + ops(p.length()); + ops(LeekFunctions.debug.getOperations()); + return null; + } + + public Object debugW(Object x) throws LeekRunException { + String p = LeekValueManager.getString(this, x); + getLogs().addLog(AILog.WARNING, p); + ops(p.length()); + ops(LeekFunctions.debug.getOperations()); + return null; + } + + public Object debugE(Object x) throws LeekRunException { + String p = LeekValueManager.getString(this, x); + getLogs().addLog(AILog.ERROR, p); + ops(p.length()); + ops(LeekFunctions.debug.getOperations()); + return null; + } + + public int color(Object red, Object green, Object blue) throws LeekRunException { + int r = integer(red); + int g = integer(green); + int b = integer(blue); + return ((r & 255) << 16) | ((g & 255) << 8) | (b & 255); + } + + public void arrayFlatten(ArrayLeekValue array, ArrayLeekValue retour, int depth) throws LeekRunException { + for (var value : array) { if (value.getValue() instanceof ArrayLeekValue && depth > 0) { - arrayFlatten(value.getArray(), retour, depth - 1); + arrayFlatten((ArrayLeekValue) value.getValue(), retour, depth - 1); } else - retour.push(this, LeekOperations.clone(this, value)); + retour.push(this, LeekOperations.clone(this, value.getValue())); } } - public AbstractLeekValue arrayFoldLeft(ArrayLeekValue array, AbstractLeekValue function, AbstractLeekValue start_value) throws Exception { - AbstractLeekValue result = LeekOperations.clone(this, start_value); - // AbstractLeekValue prev = null; - for (AbstractLeekValue value : array) { - result = function.executeFunction(this, new AbstractLeekValue[] { result, value }); + public Object arrayFoldLeft(ArrayLeekValue array, FunctionLeekValue function, Object start_value) throws LeekRunException { + Object result = LeekOperations.clone(this, start_value); + for (var value : array) { + result = function.execute(this, result, value.getValue()); } return result; } - public AbstractLeekValue arrayFoldRight(ArrayLeekValue array, AbstractLeekValue function, AbstractLeekValue start_value) throws Exception { - AbstractLeekValue result = LeekOperations.clone(this, start_value); - // AbstractLeekValue prev = null; - Iterator it = array.getReversedIterator(); + public Object arrayFoldRight(ArrayLeekValue array, FunctionLeekValue function, Object start_value) throws LeekRunException { + Object result = LeekOperations.clone(this, start_value); + // Object prev = null; + var it = array.getReversedIterator(); while (it.hasNext()) { - result = function.executeFunction(this, new AbstractLeekValue[] { it.next(), result }); + result = function.execute(this, it.next(), result); } return result; } - public AbstractLeekValue arrayPartition(ArrayLeekValue array, AbstractLeekValue function) throws Exception { + public ArrayLeekValue arrayPartitionV1(ArrayLeekValue array, FunctionLeekValue function) throws LeekRunException { ArrayLeekValue list1 = new ArrayLeekValue(); ArrayLeekValue list2 = new ArrayLeekValue(); int nb = function.getArgumentsCount(this); if (nb != 1 && nb != 2) return new ArrayLeekValue(); - VariableLeekValue value = new VariableLeekValue(this, LeekValueManager.NULL); ArrayIterator iterator = array.getArrayIterator(); boolean b; while (!iterator.ended()) { - value.set(this, iterator.getValueReference()); + var value = iterator.getValueBox(); if (nb == 1) - b = function.executeFunction(this, new AbstractLeekValue[] { value }).getBoolean(); + b = bool(LeekValueManager.execute(this, function, new Object[] { value })); else - b = function.executeFunction(this, new AbstractLeekValue[] { iterator.getKey(this), value }).getBoolean(); - iterator.setValue(this, value); - (b ? list1 : list2).getOrCreate(this, iterator.getKey(this)).set(this, iterator.getValue(this)); + b = bool(LeekValueManager.execute(this, function, new Object[] { iterator.getKey(this), value })); + (b ? list1 : list2).getOrCreate(this, iterator.getKey(this)).set(iterator.getValue(this)); iterator.next(); } - return new ArrayLeekValue(this, new AbstractLeekValue[] { list1, list2 }, false); + return new ArrayLeekValue(this, new Object[] { list1, list2 }, false); } - public ArrayLeekValue arrayMap(ArrayLeekValue array, AbstractLeekValue function) throws LeekRunException, Exception { - ArrayLeekValue retour = new ArrayLeekValue(); - ArrayIterator iterator = array.getArrayIterator(); + public ArrayLeekValue arrayPartition(ArrayLeekValue array, FunctionLeekValue function) throws LeekRunException { + ArrayLeekValue list1 = new ArrayLeekValue(); + ArrayLeekValue list2 = new ArrayLeekValue(); int nb = function.getArgumentsCount(this); if (nb != 1 && nb != 2) - return retour; - VariableLeekValue value = new VariableLeekValue(this, LeekValueManager.NULL); + return new ArrayLeekValue(); + ArrayIterator iterator = array.getArrayIterator(); + boolean b; while (!iterator.ended()) { - value.set(this, iterator.getValueReference()); + var value = iterator.value(); if (nb == 1) - retour.getOrCreate(this, iterator.getKey(this).getValue()).set(this, function.executeFunction(this, new AbstractLeekValue[] { value })); + b = bool(LeekValueManager.execute(this, function, new Object[] { value })); else - retour.getOrCreate(this, iterator.getKey(this).getValue()).set(this, function.executeFunction(this, new AbstractLeekValue[] { iterator.getKey(this), value })); - iterator.setValue(this, value); + b = bool(LeekValueManager.execute(this, function, new Object[] { iterator.getKey(this), value })); + (b ? list1 : list2).getOrCreate(this, iterator.getKey(this)).set(iterator.getValue(this)); + iterator.next(); + } + return new ArrayLeekValue(this, new Object[] { list1, list2 }, false); + } + + public ArrayLeekValue arrayMap(ArrayLeekValue array, FunctionLeekValue function) throws LeekRunException { + ArrayLeekValue retour = new ArrayLeekValue(); + ArrayIterator iterator = array.getArrayIterator(); + int nb = function.getArgumentsCount(this); + while (!iterator.ended()) { + var value = iterator.value(); + if (nb >= 2) { + retour.getOrCreate(this, iterator.getKey(this)).set(function.execute(this, iterator.getKey(this), value)); + } else { + retour.getOrCreate(this, iterator.getKey(this)).set(function.execute(this, value)); + } + iterator.next(); + } + return retour; + } + + public ArrayLeekValue arrayMapV1(ArrayLeekValue array, FunctionLeekValue function) throws LeekRunException { + ArrayLeekValue retour = new ArrayLeekValue(); + ArrayIterator iterator = array.getArrayIterator(); + int nb = function.getArgumentsCount(this); + while (!iterator.ended()) { + var value = iterator.getValueBox(); + if (nb >= 2) + retour.getOrCreate(this, iterator.getKey(this)).setRef(LeekValueManager.execute(this, function, iterator.getKey(this), value)); + else + retour.getOrCreate(this, iterator.getKey(this)).setRef(LeekValueManager.execute(this, function, value)); iterator.next(); } return retour; } - public ArrayLeekValue arrayFilter(ArrayLeekValue array, AbstractLeekValue function) throws LeekRunException, Exception { + public ArrayLeekValue arrayFilterV1(ArrayLeekValue array, FunctionLeekValue function) throws LeekRunException { ArrayLeekValue retour = new ArrayLeekValue(); ArrayIterator iterator = array.getArrayIterator(); int nb = function.getArgumentsCount(this); if (nb != 1 && nb != 2) return retour; - boolean b; - VariableLeekValue value = new VariableLeekValue(this, LeekValueManager.NULL); while (!iterator.ended()) { - value.set(this, iterator.getValueReference()); + var value = iterator.getValueBox(); if (nb == 1) { - b = function.executeFunction(this, new AbstractLeekValue[] { value }).getBoolean(); - iterator.setValue(this, value); - if (b) - retour.getOrCreate(this, iterator.getKey(this).getValue()).set(this, iterator.getValue(this).getValue()); - + if (bool(LeekValueManager.execute(this, function, new Object[] { value }))) { + // In LeekScript < 1.0, arrayFilter had a bug, the result array was not reindexed + retour.getOrCreate(this, iterator.getKey(this)).set(iterator.getValue(this)); + } } else { - b = function.executeFunction(this, new AbstractLeekValue[] { iterator.getKey(this), value }).getBoolean(); - iterator.setValue(this, value); - if (b) - retour.getOrCreate(this, iterator.getKey(this).getValue()).set(this, iterator.getValue(this).getValue()); + if (bool(LeekValueManager.execute(this, function, new Object[] { iterator.getKey(this), value }))) { + retour.getOrCreate(this, iterator.getKey(this)).set(iterator.getValue(this)); + } + } + iterator.next(); + } + return retour; + } + public ArrayLeekValue arrayFilter(ArrayLeekValue array, FunctionLeekValue function) throws LeekRunException { + ArrayLeekValue retour = new ArrayLeekValue(); + ArrayIterator iterator = array.getArrayIterator(); + int nb = function.getArgumentsCount(this); + if (nb != 1 && nb != 2) + return retour; + while (!iterator.ended()) { + var value = iterator.value(); + if (nb == 1) { + if (bool(LeekValueManager.execute(this, function, new Object[] { value }))) { + retour.push(this, iterator.getValue(this)); + } + } else { + if (bool(LeekValueManager.execute(this, function, new Object[] { iterator.getKey(this), value }))) { + retour.push(this, iterator.getValue(this)); + } } iterator.next(); } return retour; } - public AbstractLeekValue arrayIter(ArrayLeekValue array, AbstractLeekValue function) throws LeekRunException, Exception { + public Object arrayIterV1(ArrayLeekValue array, FunctionLeekValue function) throws LeekRunException { ArrayIterator iterator = array.getArrayIterator(); + if (function == null) { + return null; + } int nb = function.getArgumentsCount(this); if (nb != 1 && nb != 2) - return LeekValueManager.NULL; - VariableLeekValue value = new VariableLeekValue(this, LeekValueManager.NULL); + return null; while (!iterator.ended()) { - value.set(this, iterator.getValueReference()); - if (nb == 1) - function.executeFunction(this, new AbstractLeekValue[] { value }); - else - function.executeFunction(this, new AbstractLeekValue[] { iterator.getKey(this), value }); - iterator.setValue(this, value); + var value = iterator.getValueBox(); + if (nb == 1) { + function.execute(this, value); + } else { + function.execute(this, iterator.getKey(this), value); + } + iterator.next(); + } + return null; + } + + public Object arrayIter(ArrayLeekValue array, FunctionLeekValue function) throws LeekRunException { + ArrayIterator iterator = array.getArrayIterator(); + if (function == null) { + return null; + } + int nb = function.getArgumentsCount(this); + if (nb != 1 && nb != 2) + return null; + while (!iterator.ended()) { + var value = iterator.value(); + if (nb == 1) { + function.execute(this, value); + } else { + function.execute(this, iterator.getKey(this), value); + } iterator.next(); } - return LeekValueManager.NULL; + return null; } - public AbstractLeekValue arraySort(ArrayLeekValue origin, final AbstractLeekValue function) throws Exception { + public ArrayLeekValue arraySort(ArrayLeekValue origin, final FunctionLeekValue function) throws LeekRunException { try { int nb = function.getArgumentsCount(this); if (nb == 2) { - ArrayLeekValue array = LeekOperations.clone(this, origin).getArray(); + ArrayLeekValue array = (ArrayLeekValue) LeekOperations.clone(this, origin); array.sort(this, new Comparator() { @Override public int compare(Element o1, Element o2) { try { - return function.executeFunction(AI.this, new AbstractLeekValue[] { o1.value(), o2.value() }).getInt(AI.this); + return integer(LeekValueManager.execute(AI.this, function, o1.value(), o2.value())); } catch (Exception e) { throw new RuntimeException(e); } @@ -247,12 +647,12 @@ public int compare(Element o1, Element o2) { }); return array; } else if (nb == 4) { - ArrayLeekValue array = LeekOperations.clone(this, origin).getArray(); + ArrayLeekValue array = (ArrayLeekValue) LeekOperations.clone(this, origin); array.sort(this, new Comparator() { @Override public int compare(Element o1, Element o2) { try { - return function.executeFunction(AI.this, new AbstractLeekValue[] { o1.key(), o1.value(), o2.key(), o2.value() }).getInt(AI.this); + return integer(LeekValueManager.execute(AI.this, function, o1.key(), o1.value(), o2.key(), o2.value())); } catch (Exception e) { throw new RuntimeException(e); } @@ -265,56 +665,61 @@ public int compare(Element o1, Element o2) { throw (LeekRunException) e.getCause(); } } - return LeekValueManager.NULL; + return null; } - public AbstractLeekValue jsonEncode(AI ai, AbstractLeekValue object) { + public String jsonEncode(AI ai, Object object) { try { - String json = JSON.toJSONString(object.toJSON(ai)); - addOperations(json.length() * 10); - return new StringLeekValue(json); + String json = JSON.toJSONString(toJSON(object)); + ops(json.length() * 10); + return json; } catch (Exception e) { + // e.printStackTrace(); getLogs().addLog(AILog.ERROR, "Cannot encode object \"" + object.toString() + "\""); try { - addOperations(100); + ops(100); } catch (Exception e1) {} - return LeekValueManager.NULL; + return null; } } - public AbstractLeekValue jsonDecode(String json) { + public Object jsonDecode(String json) { try { - AbstractLeekValue obj = LeekValueManager.parseJSON(JSON.parse(json), this); - addOperations(json.length() * 10); + var obj = LeekValueManager.parseJSON(JSON.parse(json), this); + ops(json.length() * 10); return obj; } catch (Exception e) { getLogs().addLog(AILog.ERROR, "Cannot parse json \"" + json + "\""); try { - addOperations(100); + ops(100); } catch (Exception e1) {} - return LeekValueManager.NULL; + return null; } } - public void addSystemLog(int type, String key) { - addSystemLog(type, key, null); + public void addSystemLog(int type, Error error) throws LeekRunException { + addSystemLog(type, error.ordinal(), null); } - public void addSystemLog(int type, String key, String[] parameters) { - addSystemLog(type, key, parameters, Thread.currentThread().getStackTrace()); + public void addSystemLog(int type, int error) throws LeekRunException { + addSystemLog(type, error, null); } - public void addSystemLog(int type, String key, String[] parameters, StackTraceElement[] elements) { + public void addSystemLog(int type, Error error, String[] parameters) throws LeekRunException { + addSystemLog(type, error.ordinal(), parameters); + } + public void addSystemLog(int type, int error, String[] parameters) throws LeekRunException { + ops(AI.ERROR_LOG_COST); if (type == AILog.WARNING) type = AILog.SWARNING; else if (type == AILog.ERROR) @@ -322,22 +727,1267 @@ else if (type == AILog.ERROR) else if (type == AILog.STANDARD) type = AILog.SSTANDARD; - logs.addSystemLog(type, getErrorMessage(elements), key, parameters); + logs.addSystemLog(type, getErrorMessage(Thread.currentThread().getStackTrace()), error, parameters); + } + + protected String[] getErrorFiles() { return null; } + + protected String getAIString() { return ""; } + + public abstract Object runIA() throws LeekRunException; + + public int userFunctionCount(int id) { return 0; } + + public boolean[] userFunctionReference(int id) { return null; } + + public Object userFunctionExecute(int id, Object[] value) throws LeekRunException { return null; } + + public int anonymousFunctionCount(int id) { return 0; } + + public boolean[] anonymousFunctionReference(int id) { return null; } + + public RandomGenerator getRandom() { + return randomGenerator; } - - protected abstract String getErrorString(); - protected abstract String getAItring(); + public int getVersion() { return this.version; } - public abstract AbstractLeekValue runIA() throws Exception; + public static Object load(Object value) { + if (value instanceof Box) { + return ((Box) value).getValue(); + } + return value; + } + + public boolean eq(Object x, Object y) throws LeekRunException { + // ops(1); + if (x == null) return y == null; + if (x instanceof Number) { + var n = ((Number) x).doubleValue(); + if (y instanceof Number) { + return n == ((Number) y).doubleValue(); + } + if (y instanceof Boolean) { + if ((Boolean) y) return n != 0; + return n == 0; + } + if (y instanceof String) { + var s = (String) y; + if (s.equals("false") || s.equals("0") || s.equals("")) return n == 0; + if (s.equals("true")) return n != 0; + if (s.equals("1") && n == 1) return true; + if (x instanceof Double) { + try { + ops(((String) y).length()); + return n == Double.parseDouble((String) y); + } catch (Exception e) { + return false; + } + } else { + try { + ops(((String) y).length()); + return n == Integer.parseInt((String) y); + } catch (Exception e) { + return false; + } + } + } + if (y instanceof ArrayLeekValue) { + return ((ArrayLeekValue) y).equals(this, (Number) x); + } + if (y == null) return false; + return n == real(y); + } + if (x instanceof Boolean) { + if (y instanceof String) { + if (((String) y).equals("false") || ((String) y).equals("0") || ((String) y).length() == 0) return ((Boolean) x) == false; + return ((Boolean) x) == true; + } + if (y instanceof ArrayLeekValue) { + return ((ArrayLeekValue) y).equals(this, (Boolean) x); + } + if (y instanceof Number) { + return (Boolean) x == (((Number) y).doubleValue() != 0); + } + } + if (x instanceof ArrayLeekValue) { + var array = (ArrayLeekValue) x; + if (y instanceof String) { + if (((String) y).length() == 0) return array.size() == 0 || eq(array.iterator().next().getValue(), y); + } + return ((ArrayLeekValue) x).equals(this, y); + } + if (x instanceof FunctionLeekValue) { + return ((FunctionLeekValue) x).equals(this, y); + } + if (x instanceof String) { + var s = (String) x; + if (y instanceof String) { + ops(Math.min(s.length(), ((String) y).length())); + return x.equals(y); + } + if (y instanceof Number) { + var n = ((Number) y).doubleValue(); + if (s.equals("true")) return n != 0; + if (s.equals("false") || s.equals("0") || s.length() == 0) return n == 0; + if (s.equals("1") && n == 1) return true; + try { + ops(s.length()); + return n == Double.parseDouble(s); + } catch (Exception e) { + return false; + } + } + if (y instanceof Boolean) { + if (s.equals("false") || s.equals("0") || s.length() == 0) return ((Boolean) y) == false; + return ((Boolean) y) == true; + } + if (y instanceof ArrayLeekValue) { + var array = (ArrayLeekValue) y; + if (array.size() == 0) return s.length() == 0 || s.equals("false"); + if (array.size() == 1 || s.equals("true")) return eq(((ArrayLeekValue) y).iterator().next().getValue(), x); + return false; + } + } + return x.equals(y); + } + + public boolean neq(Object x, Object y) throws LeekRunException { + return !eq(x, y); + } + + public boolean equals_equals(Object x, Object y) throws LeekRunException { + if (x == null) return y == null; + if (x instanceof ObjectLeekValue && y instanceof ObjectLeekValue) { + return x.equals(y); + } + return LeekValueManager.getType(x) == LeekValueManager.getType(y) && eq(x, y); + } + + public boolean notequals_equals(Object x, Object y) throws LeekRunException { + return !equals_equals(x, y); + } + + public boolean less(Object x, Object y) throws LeekRunException { + if (x instanceof Number && y instanceof Number) { + if (x instanceof Integer && y instanceof Integer) { + return (Integer) x < (Integer) y; + } + return ((Number) x).doubleValue() < ((Number) y).doubleValue(); + } + return integer(x) < integer(y); + } + + public boolean more(Object x, Object y) throws LeekRunException { + if (x instanceof Number && y instanceof Number) { + if (x instanceof Integer && y instanceof Integer) { + return (Integer) x > (Integer) y; + } + return ((Number) x).doubleValue() > ((Number) y).doubleValue(); + } + return integer(x) > integer(y); + } + + public boolean lessequals(Object x, Object y) throws LeekRunException { + if (x instanceof Number && y instanceof Number) { + if (x instanceof Integer && y instanceof Integer) { + return (Integer) x <= (Integer) y; + } + return ((Number) x).doubleValue() <= ((Number) y).doubleValue(); + } + return integer(x) <= integer(y); + } + + public boolean moreequals(Object x, Object y) throws LeekRunException { + if (x instanceof Number && y instanceof Number) { + if (x instanceof Integer && y instanceof Integer) { + return (Integer) x >= (Integer) y; + } + return ((Number) x).doubleValue() >= ((Number) y).doubleValue(); + } + return integer(x) >= integer(y); + } + + public boolean bool(Object value) { + if (value instanceof Double) { + return (Double) value != 0; + } else if (value instanceof Integer) { + return (Integer) value != 0; + } else if (value instanceof Boolean) { + return (Boolean) value; + } else if (value instanceof ObjectLeekValue) { + return ((ObjectLeekValue) value).size() != 0; + } else if (value instanceof ArrayLeekValue) { + return ((ArrayLeekValue) value).size() != 0; + } else if (value instanceof String) { + var s = (String) value; + if (s.equals("false") || s.equals("0")) { + return false; + } + return !s.isEmpty(); + } else if (value instanceof FunctionLeekValue) { + return true; + } else if (value instanceof ClassLeekValue) { + return true; + } else if (value instanceof Box) { + return bool(((Box) value).getValue()); + } + return false; + } + + public int integer(Object value) throws LeekRunException { + if (value instanceof Double) { + return (int) (double) value; + } else if (value instanceof Integer) { + return (Integer) value; + } else if (value instanceof Boolean) { + return ((Boolean) value) ? 1 : 0; + } else if (value instanceof ObjectLeekValue) { + return ((ObjectLeekValue) value).size(); + } else if (value instanceof ArrayLeekValue) { + return ((ArrayLeekValue) value).size(); + } else if (value instanceof String) { + var s = (String) value; + // ops(2); + if (s.equals("true")) return 1; + if (s.equals("false")) return 0; + if (s.isEmpty()) return 0; + ops(s.length()); + try { + return Integer.parseInt(s); + } catch (Exception e) { + return s.length(); + } + } else if (value instanceof Box) { + return integer(((Box) value).getValue()); + } + return 0; + } - public abstract int userFunctionCount(int id); + public double real(Object value) throws LeekRunException { + if (value instanceof Double) { + return (Double) value; + } else if (value instanceof Integer) { + return (Integer) value; + } else if (value instanceof Boolean) { + return ((Boolean) value) ? 1 : 0; + } else if (value instanceof ObjectLeekValue) { + return ((ObjectLeekValue) value).size(); + } else if (value instanceof ArrayLeekValue) { + return ((ArrayLeekValue) value).size(); + } else if (value instanceof String) { + var s = (String) value; + // ai.ops(2); + if (s.equals("true")) return 1; + if (s.equals("false")) return 0; + if (s.isEmpty()) return 0; + ops(s.length()); + try { + return Double.parseDouble(s); + } catch (Exception e) { + return s.length(); + } + } else if (value instanceof Box) { + return real(((Box) value).getValue()); + } + return 0; + } - public abstract boolean[] userFunctionReference(int id); + public boolean not(Object value) throws LeekRunException { + return !bool(value); + } - public abstract AbstractLeekValue userFunctionExecute(int id, AbstractLeekValue[] value) throws Exception; + public Object minus(Object value) throws LeekRunException { + if (value instanceof Integer) return -((Integer) value); + if (value instanceof Double) return -((Double) value); + return -integer(value); + } - public abstract int anonymousFunctionCount(int id); + public int bnot(Object value) throws LeekRunException { + return LeekValueManager.bnot(this, value); + } - public abstract boolean[] anonymousFunctionReference(int id); + public int bor(Object x, Object y) throws LeekRunException { + return integer(x) | integer(y); + } + + public int band(Object x, Object y) throws LeekRunException { + return integer(x) & integer(y); + } + + public int bxor(Object x, Object y) throws LeekRunException { + return integer(x) ^ integer(y); + } + + public int shl(Object x, Object y) throws LeekRunException { + return integer(x) << integer(y); + } + + public int shr(Object x, Object y) throws LeekRunException { + return integer(x) >> integer(y); + } + + public int ushr(Object x, Object y) throws LeekRunException { + return integer(x) >>> integer(y); + } + + public Object add(Object x, Object y) throws LeekRunException { + + if (x instanceof String || y instanceof String) { + String v1_string = LeekValueManager.getString(this, x); + String v2_string = LeekValueManager.getString(this, y); + ops(v1_string.length() + v2_string.length()); + return v1_string + v2_string; + } + + + // Concatenate arrays + if (x instanceof ArrayLeekValue) { + if (y instanceof ArrayLeekValue) { + var array1 = (ArrayLeekValue) x; + var array2 = (ArrayLeekValue) y; + + ops((array1.size() + array2.size()) * 2); + + ArrayLeekValue retour = new ArrayLeekValue(); + ArrayIterator iterator = array1.getArrayIterator(); + + while (!iterator.ended()) { + if (iterator.key() instanceof String) { + retour.getOrCreate(this, iterator.getKey(this)).set(iterator.getValue(this)); + } else { + if (getVersion() == 1) { + retour.push(this, LeekOperations.clone(this, iterator.getValue(this))); + } else { + retour.push(this, iterator.getValue(this)); + } + } + iterator.next(); + } + iterator = array2.getArrayIterator(); + while (!iterator.ended()) { + if (iterator.key() instanceof String) { + retour.getOrCreate(this, iterator.getKey(this)).set(iterator.getValue(this)); + } else { + if (getVersion() == 1) { + retour.push(this, LeekOperations.clone(this, iterator.getValue(this))); + } else { + retour.push(this, iterator.getValue(this)); + } + } + iterator.next(); + } + return retour; + } + + var array1 = (ArrayLeekValue) x; + + ops(array1.size() * 2); + + ArrayLeekValue retour = new ArrayLeekValue(); + ArrayIterator iterator = array1.getArrayIterator(); + + while (!iterator.ended()) { + if (iterator.key() instanceof String) { + retour.getOrCreate(this, iterator.getKey(this)).set(iterator.getValue(this)); + } else { + if (getVersion() == 1) { + retour.push(this, LeekOperations.clone(this, iterator.getValue(this))); + } else { + retour.push(this, iterator.getValue(this)); + } + } + iterator.next(); + } + if (getVersion() == 1) { + retour.push(this, LeekOperations.clone(this, y)); + } else { + retour.push(this, y); + } + + return retour; + } + + if (y instanceof ArrayLeekValue) { + var array2 = (ArrayLeekValue) y; + + ops(array2.size() * 2); + + ArrayLeekValue retour = new ArrayLeekValue(); + + retour.push(this, x); + + ArrayIterator iterator = array2.getArrayIterator(); + while (!iterator.ended()) { + if (iterator.key() instanceof String) { + retour.getOrCreate(this, iterator.getKey(this)).set(iterator.getValue(this)); + } else { + retour.push(this, iterator.getValue(this)); + } + iterator.next(); + } + return retour; + } + + if (x instanceof Double || y instanceof Double) { + return real(x) + real(y); + } + + return integer(x) + integer(y); + } + + public Object sub(Object x, Object y) throws LeekRunException { + if (x instanceof Double || y instanceof Double) { + return real(x) - real(y); + } + return integer(x) - integer(y); + } + + public Object mul(Object x, Object y) throws LeekRunException { + if (x instanceof Double || y instanceof Double) { + return real(x) * real(y); + } + return integer(x) * integer(y); + } + + public Object div(Object x, Object y) throws LeekRunException { + double real_y = real(y); + if (version == 1 && real_y == 0) { + addSystemLog(AILog.ERROR, Error.DIVISION_BY_ZERO); + return null; + } + return real(x) / real_y; + } + + public Object mod(Object x, Object y) throws LeekRunException { + if (x instanceof Double || y instanceof Double) { + return real(x) % real(y); + } + var y_int = integer(y); + if (version == 1 && y_int == 0) { + addSystemLog(AILog.ERROR, Error.DIVISION_BY_ZERO); + return null; + } + return integer(x) % y_int; + } + + public Number pow(Object x, Object y) throws LeekRunException { + if (x instanceof Double || y instanceof Double) { + return Math.pow(real(x), real(y)); + } + return (int) Math.pow(integer(x), integer(y)); + } + + public Object add_eq(Object x, Object y) throws LeekRunException { + if (x instanceof ArrayLeekValue) { + return ((ArrayLeekValue) x).add_eq(this, y); + } + return add(x, y); + } + public Object sub_eq(Object x, Object y) throws LeekRunException { + if (x instanceof Box) { + return ((Box) x).sub_eq(y); + } + return null; + } + + public Object mul_eq(Object x, Object y) throws LeekRunException { + if (x instanceof Box) { + return ((Box) x).mul_eq(y); + } + return null; + } + + public Object div_eq(Object x, Object y) throws LeekRunException { + if (x instanceof Box) { + return ((Box) x).div_eq(y); + } + return null; + } + + public Object mod_eq(Object x, Object y) throws LeekRunException { + if (x instanceof Box) { + return ((Box) x).mod_eq(y); + } + return null; + } + + public int bor_eq(Object x, Object y) throws LeekRunException { + if (x instanceof Box) { + return ((Box) x).bor_eq(y); + } + return 0; + } + + public int band_eq(Object x, Object y) throws LeekRunException { + if (x instanceof Box) { + return ((Box) x).band_eq(y); + } + return 0; + } + + public int bxor_eq(Object x, Object y) throws LeekRunException { + if (x instanceof Box) { + return ((Box) x).bxor_eq(y); + } + return 0; + } + + public Object increment(Object x) throws LeekRunException { + if (x instanceof Box) { + return ((Box) x).increment(); + } + return null; + } + + public int intOrNull(Object value) throws LeekRunException { + if (value == null) + return -1; + return integer(value); + } + + public Object copy(Object value) throws LeekRunException { + return LeekOperations.clone(this, value); + } + + public String string(Object value) throws LeekRunException { + return LeekValueManager.getString(this, value); + } + + public String getString(Object value, Set visited) throws LeekRunException { + return LeekValueManager.getString(this, value, visited); + } + + public Object toJSON(Object v) throws LeekRunException { + if (v instanceof ArrayLeekValue) { + return ((ArrayLeekValue) v).toJSON(this, new HashSet()); + } + return v; + } + + public boolean isPrimitive(Object value) { + return !(value instanceof ArrayLeekValue || value instanceof ObjectLeekValue); + } + + public boolean isIterable(Object value) throws LeekRunException { + boolean ok = value instanceof ArrayLeekValue; + if (!ok && version >= 2) { + addSystemLog(AILog.ERROR, Error.NOT_ITERABLE, new String[] { string(value) }); + } + return ok; + } + + public Object getField(Object value, String field, ClassLeekValue fromClass) throws LeekRunException { + if (value instanceof ObjectLeekValue) { + return ((ObjectLeekValue) value).getField(field, fromClass); + } + if (value instanceof ClassLeekValue) { + return ((ClassLeekValue) value).getField(this, field, fromClass); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(value), field }); + return null; + } + + public Object setField(Object object, String field, Object value) throws LeekRunException { + if (object instanceof ObjectLeekValue) { + return ((ObjectLeekValue) object).setField(field, value); + } + if (object instanceof ClassLeekValue) { + return ((ClassLeekValue) object).setField(field, value); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(object), field }); + return null; + } + + public Object field_inc(Object object, String field) throws LeekRunException { + if (object instanceof ObjectLeekValue) { + return ((ObjectLeekValue) object).field_inc(field); + } + if (object instanceof ClassLeekValue) { + return ((ClassLeekValue) object).getFieldL(field).increment(); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(object), field }); + return null; + } + + public Object field_pre_inc(Object object, String field) throws LeekRunException { + if (object instanceof ObjectLeekValue) { + return ((ObjectLeekValue) object).field_pre_inc(field); + } + if (object instanceof ClassLeekValue) { + return ((ClassLeekValue) object).getFieldL(field).pre_increment(); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(object), field }); + return null; + } + + public Object field_dec(Object object, String field) throws LeekRunException { + if (object instanceof ObjectLeekValue) { + return ((ObjectLeekValue) object).field_dec(field); + } + if (object instanceof ClassLeekValue) { + return ((ClassLeekValue) object).getFieldL(field).decrement(); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(object), field }); + return null; + } + + public Object field_pre_dec(Object object, String field) throws LeekRunException { + if (object instanceof ObjectLeekValue) { + return ((ObjectLeekValue) object).field_pre_dec(field); + } + if (object instanceof ClassLeekValue) { + return ((ClassLeekValue) object).getFieldL(field).pre_decrement(); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(object), field }); + return null; + } + + public Object field_add_eq(Object object, String field, Object value) throws LeekRunException { + if (object instanceof ObjectLeekValue) { + return ((ObjectLeekValue) object).field_add_eq(field, value); + } + if (object instanceof ClassLeekValue) { + return ((ClassLeekValue) object).getFieldL(field).add_eq(value); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(object), field }); + return null; + } + + public Object field_sub_eq(Object object, String field, Object value) throws LeekRunException { + if (object instanceof ObjectLeekValue) { + return ((ObjectLeekValue) object).field_sub_eq(field, value); + } + if (object instanceof ClassLeekValue) { + return ((ClassLeekValue) object).getFieldL(field).sub_eq(value); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(object), field }); + return null; + } + + public Object field_mul_eq(Object object, String field, Object value) throws LeekRunException { + if (object instanceof ObjectLeekValue) { + return ((ObjectLeekValue) object).field_mul_eq(field, value); + } + if (object instanceof ClassLeekValue) { + return ((ClassLeekValue) object).getFieldL(field).mul_eq(value); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(object), field }); + return null; + } + + public Object field_pow_eq(Object object, String field, Object value) throws LeekRunException { + if (object instanceof ObjectLeekValue) { + return ((ObjectLeekValue) object).field_pow_eq(field, value); + } + if (object instanceof ClassLeekValue) { + return ((ClassLeekValue) object).getFieldL(field).pow_eq(value); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(object), field }); + return null; + } + + public Object field_div_eq(Object object, String field, Object value) throws LeekRunException { + if (object instanceof ObjectLeekValue) { + return ((ObjectLeekValue) object).field_div_eq(field, value); + } + if (object instanceof ClassLeekValue) { + return ((ClassLeekValue) object).getFieldL(field).div_eq(value); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(object), field }); + return null; + } + + public Object field_mod_eq(Object object, String field, Object value) throws LeekRunException { + if (object instanceof ObjectLeekValue) { + return ((ObjectLeekValue) object).field_mod_eq(field, value); + } + if (object instanceof ClassLeekValue) { + return ((ClassLeekValue) object).getFieldL(field).mod_eq(value); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(object), field }); + return null; + } + + public Object field_bor_eq(Object object, String field, Object value) throws LeekRunException { + if (object instanceof ObjectLeekValue) { + return ((ObjectLeekValue) object).field_bor_eq(field, value); + } + if (object instanceof ClassLeekValue) { + return ((ClassLeekValue) object).getFieldL(field).bor_eq(value); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(object), field }); + return null; + } + + public Object field_band_eq(Object object, String field, Object value) throws LeekRunException { + if (object instanceof ObjectLeekValue) { + return ((ObjectLeekValue) object).field_band_eq(field, value); + } + if (object instanceof ClassLeekValue) { + return ((ClassLeekValue) object).getFieldL(field).band_eq(value); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(object), field }); + return null; + } + + public Object field_bxor_eq(Object object, String field, Object value) throws LeekRunException { + if (object instanceof ObjectLeekValue) { + return ((ObjectLeekValue) object).field_bxor_eq(field, value); + } + if (object instanceof ClassLeekValue) { + return ((ClassLeekValue) object).getFieldL(field).bxor_eq(value); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(object), field }); + return null; + } + + public Object field_shl_eq(Object object, String field, Object value) throws LeekRunException { + if (object instanceof ObjectLeekValue) { + return ((ObjectLeekValue) object).field_shl_eq(field, value); + } + if (object instanceof ClassLeekValue) { + return ((ClassLeekValue) object).getFieldL(field).shl_eq(value); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(object), field }); + return null; + } + + public Object field_shr_eq(Object object, String field, Object value) throws LeekRunException { + if (object instanceof ObjectLeekValue) { + return ((ObjectLeekValue) object).field_shr_eq(field, value); + } + if (object instanceof ClassLeekValue) { + return ((ClassLeekValue) object).getFieldL(field).shr_eq(value); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(object), field }); + return null; + } + + public Object field_ushr_eq(Object object, String field, Object value) throws LeekRunException { + if (object instanceof ObjectLeekValue) { + return ((ObjectLeekValue) object).field_ushr_eq(field, value); + } + if (object instanceof ClassLeekValue) { + return ((ClassLeekValue) object).getFieldL(field).ushr_eq(value); + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(object), field }); + return null; + } + + public Object put(Object array, Object key, Object value) throws LeekRunException { + if (array instanceof ArrayLeekValue) { + return ((ArrayLeekValue) array).put(this, key, value); + } + if (array instanceof ObjectLeekValue) { + var field = string(key); + return ((ObjectLeekValue) array).setField(field, value); + } + if (array instanceof ClassLeekValue) { + var field = string(key); + return ((ClassLeekValue) array).setField(field, value); + } + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(array) }); + return null; + } + + public Object put_inc(Object array, Object key) throws LeekRunException { + if (array instanceof ArrayLeekValue) { + return ((ArrayLeekValue) array).put_inc(this, key); + } + if (array instanceof ObjectLeekValue) { + var field = string(key); + return ((ObjectLeekValue) array).field_inc(field); + } + if (array instanceof ClassLeekValue) { + var field = string(key); + return ((ClassLeekValue) array).field_inc(field); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(array) }); + return null; + } + + public Object put_pre_inc(Object array, Object key) throws LeekRunException { + if (array instanceof ArrayLeekValue) { + return ((ArrayLeekValue) array).put_pre_inc(this, key); + } + if (array instanceof ObjectLeekValue) { + var field = string(key); + return ((ObjectLeekValue) array).field_pre_inc(field); + } + if (array instanceof ClassLeekValue) { + var field = string(key); + return ((ClassLeekValue) array).field_pre_inc(field); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(array) }); + return null; + } + + public Object put_dec(Object array, Object key) throws LeekRunException { + if (array instanceof ArrayLeekValue) { + return ((ArrayLeekValue) array).put_dec(this, key); + } + if (array instanceof ObjectLeekValue) { + var field = string(key); + return ((ObjectLeekValue) array).field_dec(field); + } + if (array instanceof ClassLeekValue) { + var field = string(key); + return ((ClassLeekValue) array).field_dec(field); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(array) }); + return null; + } + + public Object put_pre_dec(Object array, Object key) throws LeekRunException { + if (array instanceof ArrayLeekValue) { + return ((ArrayLeekValue) array).put_pre_dec(this, key); + } + if (array instanceof ObjectLeekValue) { + var field = string(key); + return ((ObjectLeekValue) array).field_pre_dec(field); + } + if (array instanceof ClassLeekValue) { + var field = string(key); + return ((ClassLeekValue) array).field_pre_dec(field); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(array) }); + return null; + } + + public Object put_add_eq(Object array, Object key, Object value) throws LeekRunException { + if (array instanceof ArrayLeekValue) { + return ((ArrayLeekValue) array).put_add_eq(this, key, value); + } + if (array instanceof ObjectLeekValue) { + var field = string(key); + return ((ObjectLeekValue) array).field_add_eq(field, value); + } + if (array instanceof ClassLeekValue) { + var field = string(key); + return ((ClassLeekValue) array).field_add_eq(field, value); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(array) }); + return null; + } + + public Object put_sub_eq(Object array, Object key, Object value) throws LeekRunException { + if (array instanceof ArrayLeekValue) { + return ((ArrayLeekValue) array).put_sub_eq(this, key, value); + } + if (array instanceof ObjectLeekValue) { + var field = string(key); + return ((ObjectLeekValue) array).field_sub_eq(field, value); + } + if (array instanceof ClassLeekValue) { + var field = string(key); + return ((ClassLeekValue) array).field_sub_eq(field, value); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(array) }); + return null; + } + + public Object put_mul_eq(Object array, Object key, Object value) throws LeekRunException { + if (array instanceof ArrayLeekValue) { + return ((ArrayLeekValue) array).put_mul_eq(this, key, value); + } + if (array instanceof ObjectLeekValue) { + var field = string(key); + return ((ObjectLeekValue) array).field_mul_eq(field, value); + } + if (array instanceof ClassLeekValue) { + var field = string(key); + return ((ClassLeekValue) array).field_mul_eq(field, value); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(array) }); + return null; + } + + public Object put_pow_eq(Object array, Object key, Object value) throws LeekRunException { + if (array instanceof ArrayLeekValue) { + return ((ArrayLeekValue) array).put_pow_eq(this, key, value); + } + if (array instanceof ObjectLeekValue) { + var field = string(key); + return ((ObjectLeekValue) array).field_pow_eq(field, value); + } + if (array instanceof ClassLeekValue) { + var field = string(key); + return ((ClassLeekValue) array).field_pow_eq(field, value); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(array) }); + return null; + } + + public Object put_mod_eq(Object array, Object key, Object value) throws LeekRunException { + if (array instanceof ArrayLeekValue) { + return ((ArrayLeekValue) array).put_mod_eq(this, key, value); + } + if (array instanceof ObjectLeekValue) { + var field = string(key); + return ((ObjectLeekValue) array).field_mod_eq(field, value); + } + if (array instanceof ClassLeekValue) { + var field = string(key); + return ((ClassLeekValue) array).field_mod_eq(field, value); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(array) }); + return null; + } + + public Object put_div_eq(Object array, Object key, Object value) throws LeekRunException { + if (array instanceof ArrayLeekValue) { + return ((ArrayLeekValue) array).put_div_eq(this, key, value); + } + if (array instanceof ObjectLeekValue) { + var field = string(key); + return ((ObjectLeekValue) array).field_div_eq(field, value); + } + if (array instanceof ClassLeekValue) { + var field = string(key); + return ((ClassLeekValue) array).field_div_eq(field, value); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(array) }); + return null; + } + + public Object put_bor_eq(Object array, Object key, Object value) throws LeekRunException { + if (array instanceof ArrayLeekValue) { + return ((ArrayLeekValue) array).put_bor_eq(this, key, value); + } + if (array instanceof ObjectLeekValue) { + var field = string(key); + return ((ObjectLeekValue) array).field_bor_eq(field, value); + } + if (array instanceof ClassLeekValue) { + var field = string(key); + return ((ClassLeekValue) array).field_bor_eq(field, value); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(array) }); + return null; + } + + public Object put_band_eq(Object array, Object key, Object value) throws LeekRunException { + if (array instanceof ArrayLeekValue) { + return ((ArrayLeekValue) array).put_band_eq(this, key, value); + } + if (array instanceof ObjectLeekValue) { + var field = string(key); + return ((ObjectLeekValue) array).field_band_eq(field, value); + } + if (array instanceof ClassLeekValue) { + var field = string(key); + return ((ClassLeekValue) array).field_band_eq(field, value); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(array) }); + return null; + } + + public Object put_shl_eq(Object array, Object key, Object value) throws LeekRunException { + if (array instanceof ArrayLeekValue) { + return ((ArrayLeekValue) array).put_shl_eq(this, key, value); + } + if (array instanceof ObjectLeekValue) { + var field = string(key); + return ((ObjectLeekValue) array).field_shl_eq(field, value); + } + if (array instanceof ClassLeekValue) { + var field = string(key); + return ((ClassLeekValue) array).field_shl_eq(field, value); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(array) }); + return null; + } + + public Object put_shr_eq(Object array, Object key, Object value) throws LeekRunException { + if (array instanceof ArrayLeekValue) { + return ((ArrayLeekValue) array).put_shr_eq(this, key, value); + } + if (array instanceof ObjectLeekValue) { + var field = string(key); + return ((ObjectLeekValue) array).field_shr_eq(field, value); + } + if (array instanceof ClassLeekValue) { + var field = string(key); + return ((ClassLeekValue) array).field_shr_eq(field, value); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(array) }); + return null; + } + + public Object put_ushr_eq(Object array, Object key, Object value) throws LeekRunException { + if (array instanceof ArrayLeekValue) { + return ((ArrayLeekValue) array).put_ushr_eq(this, key, value); + } + if (array instanceof ObjectLeekValue) { + var field = string(key); + return ((ObjectLeekValue) array).field_ushr_eq(field, value); + } + if (array instanceof ClassLeekValue) { + var field = string(key); + return ((ClassLeekValue) array).field_ushr_eq(field, value); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(array) }); + return null; + } + + public Object put_bxor_eq(Object array, Object key, Object value) throws LeekRunException { + if (array instanceof ArrayLeekValue) { + return ((ArrayLeekValue) array).put_bxor_eq(this, key, value); + } + if (array instanceof ObjectLeekValue) { + var field = string(key); + return ((ObjectLeekValue) array).field_bxor_eq(field, value); + } + if (array instanceof ClassLeekValue) { + var field = string(key); + return ((ClassLeekValue) array).field_bxor_eq(field, value); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(array) }); + return null; + } + + public Object set(Object variable, Object value) throws LeekRunException { + if (variable instanceof Box) { + return ((Box) variable).set(value); + } + return null; + } + + public Object get(Object value, Object index, ClassLeekValue fromClass) throws LeekRunException { + if (value instanceof ArrayLeekValue) { + return ((ArrayLeekValue) value).get(this, index); + } + if (value instanceof ObjectLeekValue) { + ops(1); + return ((ObjectLeekValue) value).getField(string(index), fromClass); + } + if (value instanceof ClassLeekValue) { + ops(1); + return ((ClassLeekValue) value).getField(string(index)); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(value) }); + return null; + } + + public void profile(String function, long operations) { + if (!doProfile) return; + + String key = function; + for (StackTraceElement element : Thread.currentThread().getStackTrace()) { + if (element.getClassName().startsWith("AI_") && element.getMethodName() != "run") { + key = element.getMethodName() + ";" + key; + } + } + if (callstackOperations.containsKey(key)) { + callstackOperations.put(key, callstackOperations.get(key) + operations); + } else { + callstackOperations.put(key, operations); + } + } + + public Object profileStatic(String name, int startOp, Object value) { + profile(name, mOperations - startOp); + return value; + } + + public void generateProfile(String path) { + if (!doProfile) return; + List callstacks = new ArrayList<>(callstackOperations.keySet()); + Collections.sort(callstacks); + for (String key : callstacks) { + String parent = key.substring(0, key.lastIndexOf(";")); + if (callstackOperations.containsKey(parent)) { + callstackOperations.put(parent, callstackOperations.get(parent) - callstackOperations.get(key)); + } + } + try { + FileWriter myWriter = new FileWriter(path); + for (String key : callstacks) { + myWriter.write(key + " " + callstackOperations.get(key) + "\n"); + } + myWriter.close(); + } catch (IOException e) { + System.out.println("An error occurred."); + e.printStackTrace(); + } + } + + public Box getBox(Object value, Object index) throws LeekRunException { + if (value instanceof ArrayLeekValue) { + return ((ArrayLeekValue) value).getBox(this, index); + } + if (version >= 3) + addSystemLog(AILog.ERROR, Error.VALUE_IS_NOT_AN_ARRAY, new String[] { string(value) }); + return null; + } + + public Object callMethod(Object value, String method, ClassLeekValue fromClass, Object... args) throws LeekRunException { + if (value instanceof ObjectLeekValue) { + int startOp = mOperations; + Object retvalue = ((ObjectLeekValue) value).callMethod(method, fromClass, args); + profile("u_"+fromClass.name+"_"+method, mOperations - startOp); + return retvalue; + } + // if (value instanceof ClassLeekValue) { + // return ((ClassLeekValue) value).callMethod(method, args); + // } + return null; + } + + public Object callObjectAccess(Object value, String field, String method, ClassLeekValue fromClass, Object... args) throws LeekRunException { + if (value instanceof ClassLeekValue) { + int startOp = mOperations; + Object retvalue = ((ClassLeekValue) value).callMethod(method, fromClass, args); + profile("u_"+fromClass.name+"_"+method, mOperations - startOp); + return retvalue; + } + if (value instanceof ObjectLeekValue) { + int startOp = mOperations; + Object retvalue = ((ObjectLeekValue) value).callAccess(field, method, fromClass, args); + profile("u_"+((ObjectLeekValue) value).clazz.name+"_"+method, mOperations - startOp); + return retvalue; + } + addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { string(value), field }); + return null; + } + + public Object execute(Object function, Object... args) throws LeekRunException { + if (function instanceof ClassLeekValue) { + int startOp = mOperations; + Object retvalue = ((ClassLeekValue) function).execute(args); + profile("u_"+((ClassLeekValue) function).name+"_"+args.length, mOperations - startOp); + return retvalue; + } + if (function instanceof FunctionLeekValue) { + return ((FunctionLeekValue) function).execute(this, args); + } + addSystemLog(AILog.ERROR, Error.CAN_NOT_EXECUTE_VALUE, new String[] { string(function) }); + return null; + } + + public Object sysexec(ILeekFunction function, Object... arguments) throws LeekRunException { + // Vérification parametres + int[] parameters = function.getParameters(); + if (parameters == null || verifyParameters(parameters, arguments)) { + Object retour = function.run(this, function, arguments); + int startOp = mOperations; + function.addOperations(this, function, arguments, retour); + profile(((Enum)function).name(), mOperations - startOp); + return retour; + } else { + // Message d'erreur + String ret = LeekValue.getParamString(arguments); + addSystemLog(AILog.ERROR, Error.UNKNOWN_FUNCTION, new String[] { function + "(" + ret + ")" }); + return null; + } + } + + public boolean check(String functionName, int[] types, Object... arguments) throws LeekRunException { + if (verifyParameters(types, arguments)) { + return true; + } + String ret = LeekValue.getParamString(arguments); + addSystemLog(AILog.ERROR, Error.UNKNOWN_FUNCTION, new String[] { functionName + "(" + ret + ")" }); + return false; + } + + public static boolean verifyParameters(int[] types, Object... parameters) { + if (types.length != parameters.length) return false; + for (int i = 0; i < types.length; i++) { + if (types[i] == -1) continue; + if (i >= parameters.length || !isType(parameters[i], types[i])) { + return false; + } + } + return true; + } + + public static boolean isType(Object value, int type) { + // value = LeekValueManager.getValue(value); + switch (type) { + case BOOLEAN: return value instanceof Boolean; + case INT: return value instanceof Integer; + case DOUBLE: return value instanceof Double; + case STRING: return value instanceof String; + case NULL: return value == null; + case ARRAY: return value instanceof ArrayLeekValue; + case FUNCTION: return value instanceof FunctionLeekValue; + case NUMBER: return value instanceof Integer || value instanceof Double; + } + return true; + } + + public ClassLeekValue getClass(Object value) { + if (value == null) return nullClass; + if (value instanceof Integer) return integerClass; + if (value instanceof Double) return realClass; + if (value instanceof Boolean) return booleanClass; + if (value instanceof ArrayLeekValue) return arrayClass; + if (value instanceof String) return stringClass; + if (value instanceof ObjectLeekValue) return ((ObjectLeekValue) value).clazz; + if (value instanceof ClassLeekValue) return classClass; + if (value instanceof FunctionLeekValue) return functionClass; + return valueClass; + } + + public boolean instanceOf(Object value, Object clazz) throws LeekRunException { + ops(2); + clazz = LeekValueManager.getValue(clazz); + if (!(clazz instanceof ClassLeekValue)) { + addSystemLog(AILog.ERROR, Error.INSTANCEOF_MUST_BE_CLASS); + return false; + } + var v = load(value); + var vClass = getClass(v); + if (vClass.descendsFrom((ClassLeekValue) clazz)) { + return true; + } + return false; + } + + public long getAnalyzeTime() { + return analyzeTime; + } + + public long getCompileTime() { + return compileTime; + } + + public void setAnalyzeTime(long analyze_time) { + this.analyzeTime = analyze_time; + } + + public void setCompileTime(long compile_time) { + this.compileTime = compile_time; + } + + public void setLoadTime(long load_time) { + this.loadTime = load_time; + } + + public long getLoadTime() { + return loadTime; + } + + public void setLinesFile(File lines) { + this.filesLines = lines; + } } diff --git a/src/main/java/leekscript/runner/CallableVersion.java b/src/main/java/leekscript/runner/CallableVersion.java new file mode 100644 index 00000000..a7fbc3ed --- /dev/null +++ b/src/main/java/leekscript/runner/CallableVersion.java @@ -0,0 +1,14 @@ +package leekscript.runner; + +import leekscript.common.Type; + +public class CallableVersion { + + public Type return_type; + public Type[] arguments; + + public CallableVersion(Type return_type, Type[] arguments) { + this.return_type = return_type; + this.arguments = arguments; + } +} diff --git a/src/main/java/leekscript/runner/ILeekConstant.java b/src/main/java/leekscript/runner/ILeekConstant.java index 6884609f..33a7f807 100644 --- a/src/main/java/leekscript/runner/ILeekConstant.java +++ b/src/main/java/leekscript/runner/ILeekConstant.java @@ -1,7 +1,9 @@ package leekscript.runner; +import leekscript.common.Type; + public interface ILeekConstant { abstract public double getValue(); abstract public int getIntValue(); - abstract public int getType(); + abstract public Type getType(); } diff --git a/src/main/java/leekscript/runner/ILeekFunction.java b/src/main/java/leekscript/runner/ILeekFunction.java index f3d5b2ae..3ea7b920 100644 --- a/src/main/java/leekscript/runner/ILeekFunction.java +++ b/src/main/java/leekscript/runner/ILeekFunction.java @@ -1,13 +1,16 @@ package leekscript.runner; -import leekscript.runner.values.AbstractLeekValue; +import leekscript.common.Type; public interface ILeekFunction { + abstract public void setOperations(int operations); abstract public int getArguments(); abstract public int getArgumentsMin(); abstract public boolean isExtra(); abstract public String getNamespace(); - abstract public int[] parameters(); - abstract public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception; - abstract public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception; + abstract public int[] getParameters(); + abstract public Type getReturnType(); + abstract public CallableVersion[] getVersions(); + abstract public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException; + abstract public void addOperations(AI leekIA, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException; } diff --git a/src/main/java/leekscript/runner/LeekAnonymousFunction.java b/src/main/java/leekscript/runner/LeekAnonymousFunction.java index 92fa23fc..fee657ff 100644 --- a/src/main/java/leekscript/runner/LeekAnonymousFunction.java +++ b/src/main/java/leekscript/runner/LeekAnonymousFunction.java @@ -1,8 +1,8 @@ package leekscript.runner; -import leekscript.runner.values.AbstractLeekValue; +import leekscript.runner.values.ObjectLeekValue; public interface LeekAnonymousFunction { - public AbstractLeekValue run(AI ai, AbstractLeekValue[] values) throws Exception; + public Object run(ObjectLeekValue thiz, Object... values) throws LeekRunException; } diff --git a/src/main/java/leekscript/runner/LeekConstants.java b/src/main/java/leekscript/runner/LeekConstants.java index 0fc9cd93..0cf7c079 100644 --- a/src/main/java/leekscript/runner/LeekConstants.java +++ b/src/main/java/leekscript/runner/LeekConstants.java @@ -2,52 +2,58 @@ import java.util.HashMap; +import leekscript.common.Type; + public enum LeekConstants implements ILeekConstant { - - PI(Math.PI, LeekFunctions.DOUBLE), - E(Math.E, LeekFunctions.DOUBLE), - - INSTRUCTIONS_LIMIT(300000, LeekFunctions.INT), - OPERATIONS_LIMIT(20000000, LeekFunctions.INT), - - SORT_ASC(0, LeekFunctions.INT), - SORT_DESC(1, LeekFunctions.INT), - - CELL_EMPTY(0, LeekFunctions.INT), - CELL_PLAYER(1, LeekFunctions.INT), - CELL_OBSTACLE(2, LeekFunctions.INT), - - COLOR_RED(0xFF0000, LeekFunctions.INT), - COLOR_GREEN(0x00FF00, LeekFunctions.INT), - COLOR_BLUE(0x0000FF, LeekFunctions.INT), - - TYPE_NULL(0, LeekFunctions.INT), - TYPE_NUMBER(1, LeekFunctions.INT), - TYPE_BOOLEAN(2, LeekFunctions.INT), - TYPE_STRING(3, LeekFunctions.INT), - TYPE_ARRAY(4, LeekFunctions.INT), - TYPE_FUNCTION(5, LeekFunctions.INT); - + + PI(Math.PI, Type.REAL), + E(Math.E, Type.REAL), + + INSTRUCTIONS_LIMIT(300000, Type.INT), + OPERATIONS_LIMIT(20000000, Type.INT), + + SORT_ASC(0, Type.INT), + SORT_DESC(1, Type.INT), + + CELL_EMPTY(0, Type.INT), + CELL_PLAYER(1, Type.INT), + CELL_OBSTACLE(2, Type.INT), + + COLOR_RED(0xFF0000, Type.INT), + COLOR_GREEN(0x00FF00, Type.INT), + COLOR_BLUE(0x0000FF, Type.INT), + + TYPE_NULL(0, Type.INT), + TYPE_NUMBER(1, Type.INT), + TYPE_BOOLEAN(2, Type.INT), + TYPE_STRING(3, Type.INT), + TYPE_ARRAY(4, Type.INT), + TYPE_FUNCTION(5, Type.INT), + TYPE_CLASS(6, Type.INT), + TYPE_OBJECT(7, Type.INT); + private static HashMap extraConstants = new HashMap(); - + private double value; - private int type; - - LeekConstants(double value, int type) { + private Type type; + + LeekConstants(double value, Type type) { this.value = value; this.type = type; } - + @Override public double getValue() { return value; } + @Override public int getIntValue() { return (int) value; } + @Override - public int getType() { + public Type getType() { return type; } @@ -61,7 +67,7 @@ public static void setExtraConstants(String extraConstantsClass) { e.printStackTrace(); } } - + public static ILeekConstant get(String name) { for (LeekConstants constant : LeekConstants.values()) { if (constant.name().equals(name)) diff --git a/src/main/java/leekscript/runner/LeekFunction.java b/src/main/java/leekscript/runner/LeekFunction.java new file mode 100644 index 00000000..48887623 --- /dev/null +++ b/src/main/java/leekscript/runner/LeekFunction.java @@ -0,0 +1,6 @@ +package leekscript.runner; + +public interface LeekFunction { + + public Object run(Object... values) throws LeekRunException; +} diff --git a/src/main/java/leekscript/runner/LeekFunctions.java b/src/main/java/leekscript/runner/LeekFunctions.java index bec06e55..6703a0db 100644 --- a/src/main/java/leekscript/runner/LeekFunctions.java +++ b/src/main/java/leekscript/runner/LeekFunctions.java @@ -1,1012 +1,846 @@ package leekscript.runner; +import java.util.regex.Matcher; import java.util.regex.Pattern; import leekscript.AILog; -import leekscript.compiler.LeekScript; import leekscript.functions.Functions; import leekscript.functions.VariableOperations; -import leekscript.runner.values.AbstractLeekValue; import leekscript.runner.values.ArrayLeekValue; -import leekscript.runner.values.BooleanLeekValue; -import leekscript.runner.values.DoubleLeekValue; import leekscript.runner.values.FunctionLeekValue; -import leekscript.runner.values.IntLeekValue; -import leekscript.runner.values.NullLeekValue; -import leekscript.runner.values.StringLeekValue; +import leekscript.common.Error; +import leekscript.common.Type; public enum LeekFunctions implements ILeekFunction { - // Fonctions mathématiques - abs(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekDoubleValue(Math.abs(parameters[0].getDouble(leekIA))); - } - @Override - public int[] parameters() { - return new int[] { NUMBER }; + // Fonctions mathématiques + // abs(new CallableVersion[] { + // new CallableVersion(Type.INT, new Type[] { Type.INT }), + // new CallableVersion(Type.REAL, new Type[] { Type.REAL }) + // }), + abs(1, new int[] { AI.NUMBER }) { + @Override + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + if (parameters[0] instanceof Integer) { + return Math.abs((Integer) parameters[0]); + } + return Math.abs(ai.real(parameters[0])); } }, - min(2) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekDoubleValue(Math.min(parameters[0].getDouble(leekIA), parameters[1].getDouble(leekIA))); - } + min(2, new int[] { AI.NUMBER, AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER, NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + if (parameters[0] instanceof Integer && parameters[1] instanceof Integer) { + var v1 = ((Integer) parameters[0]).intValue(); + var v2 = ((Integer) parameters[1]).intValue(); + return Math.min(v1, v2); + } + var v1 = ((Number) parameters[0]).doubleValue(); + var v2 = ((Number) parameters[1]).doubleValue(); + return Math.min(v1, v2); } }, - max(2) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekDoubleValue(Math.max(parameters[0].getDouble(leekIA), parameters[1].getDouble(leekIA))); - } + max(2, new int[] { AI.NUMBER, AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER, NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + if (parameters[0] instanceof Integer && parameters[1] instanceof Integer) { + var v1 = ((Integer) parameters[0]).intValue(); + var v2 = ((Integer) parameters[1]).intValue(); + return Math.max(v1, v2); + } + double v1 = ((Number) parameters[0]).doubleValue(); + double v2 = ((Number) parameters[1]).doubleValue(); + return Math.max(v1, v2); } }, - cos(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new DoubleLeekValue(Math.cos(parameters[0].getDouble(leekIA))); - } + // cos(new CallableVersion[] { + // new CallableVersion(Type.REAL, new Type[] { Type.REAL }) + // }), + cos(1, new int[] { AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ai.real(parameters[0]); + return Math.cos(v); } }, - sin(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new DoubleLeekValue(Math.sin(parameters[0].getDouble(leekIA))); - } + sin(1, new int[] { AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ai.real(parameters[0]); + return Math.sin(v); } }, - tan(1) { + tan(1, new int[] { AI.NUMBER }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new DoubleLeekValue(Math.tan(parameters[0].getDouble(leekIA))); - } - - @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ai.real(parameters[0]); + return Math.tan(v); } }, - toRadians(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new DoubleLeekValue(parameters[0].getDouble(leekIA) * Math.PI / 180); - } - + toRadians(1, new int[] { AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ai.real(parameters[0]); + return v * Math.PI / 180; } }, - toDegrees(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new DoubleLeekValue(parameters[0].getDouble(leekIA) * 180 / Math.PI); - } - + toDegrees(1, new int[] { AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ai.real(parameters[0]); + return v * 180 / Math.PI; } }, - acos(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new DoubleLeekValue(Math.acos(parameters[0].getDouble(leekIA))); - } + // acos(new CallableVersion[] { + // new CallableVersion(Type.REAL, new Type[] { Type.REAL }) + // }), + acos(1, new int[] { AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ai.real(parameters[0]); + return Math.acos(v); } }, - asin(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new DoubleLeekValue(Math.asin(parameters[0].getDouble(leekIA))); - } + // asin(new CallableVersion[] { + // new CallableVersion(Type.REAL, new Type[] { Type.REAL }) + // }), + asin(1, new int[] { AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ai.real(parameters[0]); + return Math.asin(v); } }, - atan(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new DoubleLeekValue(Math.atan(parameters[0].getDouble(leekIA))); - } + // atan(new CallableVersion[] { + // new CallableVersion(Type.REAL, new Type[] { Type.REAL }) + // }), + atan(1, new int[] { AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ai.real(parameters[0]); + return Math.atan(v); } }, - atan2(2) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new DoubleLeekValue(Math.atan2(parameters[0].getDouble(leekIA), parameters[1].getDouble(leekIA))); - } + atan2(2, new int[] { AI.NUMBER, AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER, NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double y = ai.real(parameters[0]); + double x = ai.real(parameters[1]); + return Math.atan2(y, x); } }, - ceil(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekIntValue((int) Math.ceil(parameters[0].getDouble(leekIA))); - } + // ceil(new CallableVersion[] { + // new CallableVersion(Type.INT, new Type[] { Type.INT }), + // new CallableVersion(Type.INT, new Type[] { Type.REAL }), + // }), + ceil(1, new int[] { AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ai.real(parameters[0]); + return (int) Math.ceil(v); } }, - floor(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekIntValue((int) Math.floor(parameters[0].getDouble(leekIA))); - } + // floor(new CallableVersion[] { + // new CallableVersion(Type.INT, new Type[] { Type.INT }), + // new CallableVersion(Type.INT, new Type[] { Type.REAL }), + // }), + floor(1, new int[] { AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ai.real(parameters[0]); + return (int) Math.floor(v); } }, - round(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekIntValue((int) Math.round(parameters[0].getDouble(leekIA))); - } + // round(new CallableVersion[] { + // new CallableVersion(Type.INT, new Type[] { Type.INT }), + // new CallableVersion(Type.INT, new Type[] { Type.REAL }), + // }), + round(1, new int[] { AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ai.real(parameters[0]); + return (int) Math.round(v); } }, - sqrt(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekDoubleValue(Math.sqrt(parameters[0].getDouble(leekIA))); - } + sqrt(1, new int[] { AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ai.real(parameters[0]); + return Math.sqrt(v); } }, - cbrt(1) { + cbrt(1, new int[] { AI.NUMBER }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new DoubleLeekValue(Math.cbrt(parameters[0].getDouble(leekIA))); - } - - @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ai.real(parameters[0]); + return Math.cbrt(v); } }, - log(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new DoubleLeekValue(Math.log(parameters[0].getDouble(leekIA))); - } - + log(1, new int[] { AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ai.real(parameters[0]); + return Math.log(v); } }, - log10(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new DoubleLeekValue(Math.log10(parameters[0].getDouble(leekIA))); - } - + log2(1, new int[] { AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ai.real(parameters[0]); + return Math.log(v) / Math.log(2); } }, - exp(1) { + log10(1, new int[] { AI.NUMBER }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new DoubleLeekValue(Math.exp(parameters[0].getDouble(leekIA))); - } - - @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ai.real(parameters[0]); + return Math.log10(v); } }, - pow(2) { + exp(1, new int[] { AI.NUMBER }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekDoubleValue(Math.pow(parameters[0].getDouble(leekIA), parameters[1].getDouble(leekIA))); + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ai.real(parameters[0]); + return Math.exp(v); } + }, + pow(2, new int[] { AI.NUMBER, AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER, NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double x = ai.real(parameters[0]); + double y = ai.real(parameters[1]); + return Math.pow(x, y); } }, + rand(0) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new DoubleLeekValue(LeekScript.getRandom().getDouble()); + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + return ai.getRandom().getDouble(); } }, - randInt(2) { + randInt(2, new int[] { AI.NUMBER, AI.NUMBER }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - int nb = parameters[0].getInt(leekIA); - int nb1 = parameters[1].getInt(leekIA); + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + int nb = ai.integer(parameters[0]); + int nb1 = ai.integer(parameters[1]); if (nb > nb1) - return LeekValueManager.getLeekIntValue(LeekScript.getRandom().getInt(nb1, nb - 1)); + return ai.getRandom().getInt(nb1, nb - 1); else - return LeekValueManager.getLeekIntValue(LeekScript.getRandom().getInt(nb, nb1 - 1)); - } - - @Override - public int[] parameters() { - return new int[] { NUMBER, NUMBER }; + return ai.getRandom().getInt(nb, nb1 - 1); } }, - randFloat(2) { + randFloat(2, new int[] { AI.NUMBER, AI.NUMBER }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new DoubleLeekValue(parameters[0].getDouble(leekIA) + LeekScript.getRandom().getDouble() * (parameters[1].getDouble(leekIA) - parameters[0].getDouble(leekIA))); - } - - @Override - public int[] parameters() { - return new int[] { NUMBER, NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double a = ai.real(parameters[0]); + double b = ai.real(parameters[1]); + return a + ai.getRandom().getDouble() * (b - a); } }, - hypot(2) { + hypot(2, new int[] { AI.NUMBER, AI.NUMBER }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new DoubleLeekValue(Math.hypot(parameters[0].getDouble(leekIA), parameters[1].getDouble(leekIA))); - } - - @Override - public int[] parameters() { - return new int[] { NUMBER, NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double x = ai.real(parameters[0]); + double y = ai.real(parameters[1]); + return Math.hypot(x, y); } }, - signum(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekIntValue((int) Math.signum(parameters[0].getDouble(leekIA))); - } - + signum(1, new int[] { AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + double v = ((Number) parameters[0]).doubleValue(); + return (int) Math.signum(v); } }, string(1) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new StringLeekValue(parameters[0].getString(leekIA)); + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + return ai.string(parameters[0]); } }, - // Fonctions string - charAt(2) { + // Fonctions string + charAt(2, new int[] { AI.STRING, AI.NUMBER }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - int pos = parameters[1].getInt(leekIA); - String str = parameters[0].getString(leekIA); + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + int pos = ai.integer(parameters[1]); + String str = (String) parameters[0]; if (pos < 0 || pos >= str.length()) - return LeekValueManager.NULL; - return new StringLeekValue(String.valueOf(str.charAt(pos))); - } - - @Override - public int[] parameters() { - return new int[] { STRING, NUMBER }; + return null; + return String.valueOf(str.charAt(pos)); } }, - length(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekIntValue(parameters[0].getString(leekIA).length()); - } - + length(1, new int[] { AI.STRING }) { @Override - public int[] parameters() { - return new int[] { STRING }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + String v = LeekValueManager.getString(ai, parameters[0]); + return v.length(); } }, - substring(2, 3) { + substring(2, 3, new int[] { AI.STRING, AI.NUMBER, -1 }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - if (verifyParameters(new int[] { STRING, NUMBER, NUMBER }, parameters)) { - String string = parameters[0].getString(leekIA); - int index = parameters[1].getInt(leekIA); - int length = parameters[2].getInt(leekIA); - if (string.length() <= index || index < 0) { - return LeekValueManager.NULL; - } - if (index + length > string.length()) { - return LeekValueManager.NULL; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + if (AI.verifyParameters(new int[] { AI.STRING, AI.NUMBER, AI.NUMBER }, parameters)) { + String string = LeekValueManager.getString(ai, parameters[0]); + int index = ai.integer(parameters[1]); + int length = ai.integer(parameters[2]); + if (string.length() <= index || index < 0 || index + length > string.length() || length < 0) { + return null; } - return new StringLeekValue(string.substring(index, index + length)); + return string.substring(index, index + length); } else { - int index = parameters[1].getInt(leekIA); - if (parameters[0].getString(leekIA).length() <= index || index < 0) { - return LeekValueManager.NULL; + String string = LeekValueManager.getString(ai, parameters[0]); + int index = ai.integer(parameters[1]); + if (string.length() <= index || index < 0) { + return null; } - return new StringLeekValue(parameters[0].getString(leekIA).substring(index)); + return string.substring(index); } } + @Override - public int[] parameters() { - return new int[] { STRING, NUMBER, -1 }; - } - @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? mVariableOperations.getOperations( - retour.getString(leekIA).length()) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + String s = LeekValueManager.getString(ai, retour); + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations(s.length()) : 1); } }, - replace(3) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new StringLeekValue(parameters[0].getString(leekIA) - .replaceAll(Pattern.quote(parameters[1] - .getString(leekIA)), - parameters[2].getString(leekIA))); - } - + replace(3, new int[] { AI.STRING, AI.STRING, AI.STRING }) { @Override - public int[] parameters() { - return new int[] { STRING, STRING, STRING }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + String s = LeekValueManager.getString(ai, parameters[0]); + String pattern = LeekValueManager.getString(ai, parameters[1]); + String replacement = LeekValueManager.getString(ai, parameters[2]); + return s.replaceAll(Pattern.quote(pattern), Matcher.quoteReplacement(replacement)); } @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? - mVariableOperations.getOperations( - parameters[0].getString(leekIA).length()) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + String s = LeekValueManager.getString(ai, parameters[0]); + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations(s.length()) : 1); } }, - indexOf(2, 3) { - + indexOf(2, 3, new int[] { AI.STRING, AI.STRING, -1 }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - if (verifyParameters(new int[] { STRING, STRING, NUMBER }, parameters)) { - return LeekValueManager.getLeekIntValue(parameters[0].getString(leekIA) - .indexOf(parameters[1].getString(leekIA), parameters[2].getInt(leekIA))); + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + String s = (String) parameters[0]; + String needle = (String) parameters[1]; + int from = ai.integer(parameters[2]); + if (AI.verifyParameters(new int[] { AI.STRING, AI.STRING, AI.NUMBER }, parameters)) { + return s.indexOf(needle, from); } else { - return LeekValueManager.getLeekIntValue(parameters[0].getString(leekIA) - .indexOf(parameters[1].getString(leekIA))); + return s.indexOf(needle); } } @Override - public int[] parameters() { - return new int[] { STRING, STRING, -1 }; - } - - @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? mVariableOperations.getOperations((parameters[0].getString(leekIA).length())) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + String s = LeekValueManager.getString(ai, parameters[0]); + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations(s.length()) : 1); } }, - split(2, 3) { + split(2, 3, new int[] { AI.STRING, AI.STRING, -1 }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - if (verifyParameters(new int[] { STRING, STRING, NUMBER }, parameters)) { - String[] elements = parameters[0].getString(leekIA) - .split(parameters[1].getString(leekIA), parameters[2].getInt(leekIA)); + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + String s = LeekValueManager.getString(ai, parameters[0]); + String delimiter = LeekValueManager.getString(ai, parameters[1]); + if (AI.verifyParameters(new int[] { AI.STRING, AI.STRING, AI.NUMBER }, parameters)) { + int limit = ai.integer(parameters[2]); + String[] elements = s.split(delimiter, limit); ArrayLeekValue array = new ArrayLeekValue(); for (short i = 0; i < elements.length; i++) { - array.push(leekIA, new StringLeekValue(elements[i])); + array.push(ai, elements[i]); } return array; } else { - String[] elements = parameters[0].getString(leekIA) - .split(Pattern.quote(parameters[1].getString(leekIA))); + String[] elements = s.split(Pattern.quote(delimiter)); ArrayLeekValue array = new ArrayLeekValue(); for (short i = 0; i < elements.length; i++) { - array.push(leekIA, new StringLeekValue(elements[i])); + array.push(ai, elements[i]); } return array; } } + @Override - public int[] parameters() { - return new int[] { STRING, STRING, -1 }; - } - @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations( - hasVariableOperations() ? - mVariableOperations.getOperations( - (int) (parameters[0].getString(leekIA).length() - * Math.log(parameters[1].getString(leekIA).length() + 1))) - : 1); - if (retour.isArray()) - leekIA.addOperations(retour.getArray().size()); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + String s = LeekValueManager.getString(ai, parameters[0]); + String delimiter = LeekValueManager.getString(ai, parameters[1]); + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations((int) (s.length() * Math.log(delimiter.length() + 1))) : 1); + if (retour instanceof ArrayLeekValue) { + ai.ops(((ArrayLeekValue) retour).size()); + } } }, - toLower(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new StringLeekValue(parameters[0].getString(leekIA).toLowerCase()); - } - + toLower(1, new int[] { AI.STRING }) { @Override - public int[] parameters() { - return new int[] { STRING }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + String s = LeekValueManager.getString(ai, parameters[0]); + return s.toLowerCase(); } @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? - mVariableOperations.getOperations(retour.getString(leekIA).length()) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations(((String) retour).length()) : 1); } }, - toUpper(1) { + toUpper(1, new int[] { AI.STRING }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new StringLeekValue(parameters[0].getString(leekIA).toUpperCase()); + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + String s = LeekValueManager.getString(ai, parameters[0]); + return s.toUpperCase(); } @Override - public int[] parameters() { - return new int[] { STRING }; - } - - @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() - ? mVariableOperations.getOperations(retour.getString(leekIA).length()) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations(((String) retour).length()) : 1); } }, - startsWith(2) { + startsWith(2, new int[] { AI.STRING, AI.STRING }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekBooleanValue( - parameters[0].getString(leekIA) - .startsWith(parameters[1].getString(leekIA))); + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + String s = LeekValueManager.getString(ai, parameters[0]); + String prefix = LeekValueManager.getString(ai, parameters[1]); + return s.startsWith(prefix); } @Override - public int[] parameters() { - return new int[] { STRING, STRING }; - } - - @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - if (parameters[0].getString(leekIA).length() > parameters[1].getString(leekIA).length()) { - leekIA.addOperations(hasVariableOperations() ? - mVariableOperations.getOperations((parameters[1].getString(leekIA).length())) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + String s = LeekValueManager.getString(ai, parameters[0]); + String prefix = LeekValueManager.getString(ai, parameters[1]); + if (s.length() > prefix.length()) { + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations((prefix.length())) : 1); } else - leekIA.addOperations(1); + ai.ops(1); } }, - endsWith(2) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekBooleanValue(parameters[0].getString(leekIA) - .endsWith(parameters[1].getString(leekIA))); - } - + endsWith(2, new int[] { AI.STRING, AI.STRING }) { @Override - public int[] parameters() { - return new int[] { STRING, STRING }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + String s = LeekValueManager.getString(ai, parameters[0]); + String suffix = LeekValueManager.getString(ai, parameters[1]); + return s.endsWith(suffix); } @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - if (parameters[0].getString(leekIA).length() > parameters[1].getString(leekIA).length()) { - leekIA.addOperations(hasVariableOperations() ? - mVariableOperations.getOperations((parameters[1].getString(leekIA).length())) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + String s = LeekValueManager.getString(ai, parameters[0]); + String suffix = LeekValueManager.getString(ai, parameters[1]); + if (s.length() > suffix.length()) { + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations((suffix.length())) : 1); } else - leekIA.addOperations(1); + ai.ops(1); } }, - contains(2) { + contains(2, new int[] { AI.STRING, AI.STRING }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - String haystack = parameters[0].getString(leekIA); - String needle = parameters[1].getString(leekIA); - if (needle.length() > haystack.length()) { - return LeekValueManager.getLeekBooleanValue(false); - } - return LeekValueManager.getLeekBooleanValue(haystack.contains(needle)); - } - @Override - public int[] parameters() { - return new int[] { STRING, STRING }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + String haystack = LeekValueManager.getString(ai, parameters[0]); + String needle = LeekValueManager.getString(ai, parameters[1]); + return haystack.contains(needle); } + @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? - mVariableOperations.getOperations( - (int) (parameters[0].getString(leekIA).length() - * Math.log(parameters[1].getString(leekIA).length() + 1))) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + String haystack = LeekValueManager.getString(ai, parameters[0]); + String needle = LeekValueManager.getString(ai, parameters[1]); + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations((int) (haystack.length() * Math.log(needle.length() + 1))) : 1); } }, number(1) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - if (parameters[0].getType() == AbstractLeekValue.NUMBER) - return LeekOperations.clone(leekIA, parameters[0].getValue()); - if (parameters[0].getType() != AbstractLeekValue.STRING) - return LeekValueManager.NULL; - try { - if (parameters[0].getString(leekIA).contains(".")) { - return LeekValueManager.getLeekDoubleValue(Double.parseDouble(parameters[0].getString(leekIA))); - } else { - return LeekValueManager.getLeekIntValue(Integer.parseInt(parameters[0].getString(leekIA))); - } - } catch (Exception e) { - return LeekValueManager.NULL; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + var v = parameters[0]; + if (v instanceof Number) + return v; + if (v instanceof String) { + var s = (String) v; + try { + if (s.contains(".")) { + return Double.parseDouble(s); + } else { + return Integer.parseInt(s); + } + } catch (Exception e) {} } - } - - @Override - public int[] parameters() { - return new int[] { -1 }; + return null; } }, // Fonctions array - remove(2) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return parameters[0].getArray().remove(leekIA, parameters[1].getInt(leekIA)); - } - + remove(2, new int[] { AI.ARRAY, AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { ARRAY, NUMBER }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + var index = ((Number) parameters[1]).intValue(); + return array.remove(ai, index); } @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - if (parameters[1].getInt(leekIA) >= 0 && parameters[1].getInt(leekIA) < parameters[0].getArray().size()) { - leekIA.addOperations(hasVariableOperations() ? mVariableOperations.getOperations(parameters[1].getInt(leekIA) + 1) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + var index = ((Number) parameters[1]).intValue(); + if (index >= 0 && index < array.size()) { + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations(index + 1) : 1); } else - leekIA.addOperations(1); - } - }, - count(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekIntValue(parameters[0].getArray().size()); - } - - @Override - public int[] parameters() { - return new int[] { ARRAY }; + ai.ops(1); } }, - join(2) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new StringLeekValue(parameters[0].getArray().join(leekIA, parameters[1].getString(leekIA))); - } - - @Override - public int[] parameters() { - return new int[] { ARRAY, STRING }; - } + // count(new CallableVersion[] { new CallableVersion(Type.ANY, new Type[] { Type.ARRAY }) }), + count(1, new int[] { AI.ARRAY }) { @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? mVariableOperations.getOperations(retour.getString(leekIA).length() + 1) : 1); + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + return array.size(); } }, - insert(3) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - parameters[0].getArray().insert(leekIA, LeekOperations.clone(leekIA, parameters[1]), parameters[2].getInt(leekIA)); - return LeekValueManager.NULL; - } + join(2, new int[] { AI.ARRAY, AI.STRING }) { @Override - public int[] parameters() { - return new int[] { ARRAY, -1, NUMBER }; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + var delimiter = (String) parameters[1]; + return array.join(leekIA, delimiter); } @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - if (parameters[2].getInt(leekIA) >= 0 && parameters[2].getInt(leekIA) < parameters[0].getArray().size()) { - leekIA.addOperations(hasVariableOperations() ? mVariableOperations.getOperations(parameters[1].getInt(leekIA) + 1) : 1); - } else - leekIA.addOperations(1); + public void addOperations(AI leekIA, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + var r = (String) retour; + leekIA.ops(hasVariableOperations() ? mVariableOperations.getOperations(r.length() + 1) : 1); } }, - push(2) { + insert(3, new int[] { AI.ARRAY, -1, AI.NUMBER }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - parameters[0].getArray().push(leekIA, LeekOperations.clone(leekIA, parameters[1])); - return LeekValueManager.NULL; - } - - @Override - public int[] parameters() { - return new int[] { ARRAY, -1 }; - } - }, - unshift(2) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - parameters[0].getArray().insert(leekIA, LeekOperations.clone(leekIA, parameters[1]), 0); - return LeekValueManager.NULL; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + var index = ((Number) parameters[2]).intValue(); + if (ai.getVersion() == 1) { + array.insert(ai, LeekOperations.clone(ai, parameters[1]), index); + } else { + array.insert(ai, parameters[1], index); + } + return null; } @Override - public int[] parameters() { - return new int[] { ARRAY, -1 }; + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + var index = ((Number) parameters[2]).intValue(); + ai.ops(1 + (array.size() - index) * 4); } }, - shift(1) { + push(2, new int[] { AI.ARRAY, -1 }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - if (parameters[0].getArray().size() > 0) { - AbstractLeekValue v = parameters[0].getArray().start().getValue(); - parameters[0].getArray().remove(leekIA, 0); - return v; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + if (ai.getVersion() >= 2) { + array.push(ai, parameters[1]); + } else { + array.push(ai, LeekOperations.clone(ai, parameters[1])); } - return LeekValueManager.NULL; - } - - @Override - public int[] parameters() { - return new int[] { ARRAY }; + return null; } }, - pop(1) { + unshift(2, new int[] { AI.ARRAY, -1 }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - if (parameters[0].getArray().size() > 0) { - AbstractLeekValue v = parameters[0].getArray().end().getValue(); - parameters[0].getArray().remove(leekIA, parameters[0].getArray().size() - 1); - return v; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + if (leekIA.getVersion() == 1) { + var value = LeekOperations.clone(leekIA, parameters[1]); + array.insert(leekIA, value, 0); + } else { + array.insert(leekIA, parameters[1], 0); } - return LeekValueManager.NULL; - } - - @Override - public int[] parameters() { - return new int[] { ARRAY }; + return null; } }, - removeElement(2) { + shift(1, new int[] { AI.ARRAY }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - parameters[0].getArray().removeObject(leekIA, parameters[1]); - return LeekValueManager.NULL; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + return array.remove(leekIA, 0); } - - @Override - public int[] parameters() { - return new int[] { ARRAY, -1 }; - } - + }, + pop(1, new int[] { AI.ARRAY }) { @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? - mVariableOperations.getOperations(parameters[0].getArray().size() + 1) : 1); + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + return array.remove(leekIA, array.size() - 1); } }, - removeByKey(2) { + removeElement(2, new int[] { AI.ARRAY, -1 }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - parameters[0].getArray().removeByKey(leekIA, parameters[1]); - return LeekValueManager.NULL; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + array.removeObject(ai, parameters[1]); + return null; } @Override - public int[] parameters() { - return new int[] { ARRAY, -1 }; + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations(array.size() + 1) : 1); } }, - removeKey(2) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - parameters[0].getArray().removeByKey(leekIA, parameters[1]); - return LeekValueManager.NULL; - } - + removeKey(2, new int[] { AI.ARRAY, -1 }) { @Override - public int[] parameters() { - return new int[] { ARRAY, -1 }; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + array.removeByKey(leekIA, parameters[1]); + return null; } }, - sort(1, 2) { + sort(1, 2, new int[] { AI.ARRAY, -1 }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; int type = LeekValueComparator.SortComparator.SORT_ASC; - if (parameters[1].getBoolean()) + if (leekIA.bool(parameters[1])) type = LeekValueComparator.SortComparator.SORT_DESC; - parameters[0].getArray().sort(leekIA, type); - return LeekValueManager.NULL; - } - - @Override - public int[] parameters() { - return new int[] { ARRAY, -1 }; + array.sort(leekIA, type); + return null; } @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? mVariableOperations.getOperations(parameters[0].getArray().size() + 1) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations(array.size() + 1) : 1); } }, - assocSort(1, 2) { + assocSort(1, 2, new int[] { AI.ARRAY, -1 }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; int type = PhpArray.ASC_A; - if (parameters[1].getBoolean()) + if (leekIA.bool(parameters[1])) type = PhpArray.DESC_A; - parameters[0].getArray().sort(leekIA, type); - return LeekValueManager.NULL; - } + // try { + array.sort(leekIA, type); + // } catch (Exception e) { - @Override - public int[] parameters() { - return new int[] { ARRAY, -1 }; + // e.printStackTrace(System.out); + // } + return null; } @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? mVariableOperations.getOperations(parameters[0].getArray().size() + 1) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations(array.size() + 1) : 1); } }, - keySort(1, 2) { + keySort(1, 2, new int[] { AI.ARRAY, -1 }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; int type = PhpArray.ASC_K; - if (parameters[1].getBoolean()) + if (leekIA.bool(parameters[1])) type = PhpArray.DESC_K; - parameters[0].getArray().sort(leekIA, type); - return LeekValueManager.NULL; - } - - @Override - public int[] parameters() { - return new int[] { ARRAY, -1 }; + array.sort(leekIA, type); + return null; } @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? mVariableOperations.getOperations(parameters[0].getArray().size() + 1) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations(array.size() + 1) : 1); } }, - shuffle(1) { + shuffle(1, new int[] { AI.ARRAY }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - parameters[0].getArray().shuffle(leekIA); - return LeekValueManager.NULL; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + array.shuffle(leekIA); + return null; } @Override - public int[] parameters() { - return new int[] { ARRAY }; - } - - @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? mVariableOperations.getOperations(parameters[0].getArray().size() + 1) : 1); + public void addOperations(AI leekIA, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + leekIA.ops(hasVariableOperations() ? mVariableOperations.getOperations(array.size() + 1) : 1); } }, - search(2, 3) { + search(2, 3, new int[] { AI.ARRAY, -1, -1 }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - if (verifyParameters(new int[] { ARRAY, -1, NUMBER }, parameters)) { - return parameters[0].getArray().search(leekIA, parameters[1], parameters[2].getInt(leekIA)); + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + if (AI.verifyParameters(new int[] { AI.ARRAY, -1, AI.NUMBER }, parameters)) { + var index = ai.integer(parameters[2]); + return array.search(ai, parameters[1], index); } else { - return parameters[0].getArray().search(leekIA, parameters[1], 0); + return array.search(ai, parameters[1], 0); } } @Override - public int[] parameters() { - return new int[] { ARRAY, -1, -1 }; - } - - @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? mVariableOperations.getOperations(parameters[0].getArray().size() + 1) : 1); + public void addOperations(AI leekIA, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + leekIA.ops(hasVariableOperations() ? mVariableOperations.getOperations(array.size() + 1) : 1); } }, - inArray(2) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekBooleanValue(parameters[0].getArray().contains(leekIA, parameters[1])); - } - + inArray(2, new int[] { AI.ARRAY, -1 }) { @Override - public int[] parameters() { - return new int[] { ARRAY, -1 }; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + return array.contains(leekIA, parameters[1]); } @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? mVariableOperations.getOperations(parameters[0].getArray().size() + 1) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations(array.size() + 1) : 1); } }, - reverse(1) { + reverse(1, new int[] { AI.ARRAY }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - parameters[0].getArray().reverse(leekIA); - return LeekValueManager.NULL; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + array.reverse(leekIA); + return null; } @Override - public int[] parameters() { - return new int[] { ARRAY }; - } - - @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? mVariableOperations.getOperations(parameters[0].getArray().size() + 1) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations(array.size() + 1) : 1); } }, - arrayMin(1) { + arrayMin(1, new int[] { AI.ARRAY }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - AbstractLeekValue max_c = null; - LeekValueComparator.SortComparator comp = new LeekValueComparator.SortComparator(leekIA, LeekValueComparator.SortComparator.SORT_ASC); - for (AbstractLeekValue val : parameters[0].getArray()) { - if (max_c == null) - max_c = val.getValue(); - else if (comp.compare(val.getValue(), max_c) == -1) - max_c = val.getValue(); + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + if (array.size() > 0) { + var comp = new LeekValueComparator.SortComparator(ai, LeekValueComparator.SortComparator.SORT_ASC); + var iterator = array.iterator(); + Object min_c = iterator.next().getValue(); + while (iterator.hasNext()) { + var value = iterator.next().getValue(); + if (comp.compare(value, min_c) == -1) { + min_c = value; + } + } + return LeekOperations.clone(ai, min_c); } - if (max_c == null) - return LeekValueManager.NULL; - else - return LeekOperations.clone(leekIA, max_c); + return null; } @Override - public int[] parameters() { - return new int[] { ARRAY }; + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations(array.size() + 1) : 1); } }, - arrayMax(1) { + arrayMax(1, new int[] { AI.ARRAY }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - AbstractLeekValue min_c = null; - LeekValueComparator.SortComparator mincomp = new LeekValueComparator.SortComparator(leekIA, LeekValueComparator.SortComparator.SORT_ASC); - for (AbstractLeekValue val : parameters[0].getArray()) { + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + Object min_c = null; + var mincomp = new LeekValueComparator.SortComparator(leekIA, LeekValueComparator.SortComparator.SORT_ASC); + for (var val : array) { if (min_c == null) min_c = val.getValue(); else if (mincomp.compare(val.getValue(), min_c) == 1) min_c = val.getValue(); } if (min_c == null) - return LeekValueManager.NULL; + return null; else return LeekOperations.clone(leekIA, min_c); } @Override - public int[] parameters() { - return new int[] { ARRAY }; - } - - @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? mVariableOperations.getOperations(parameters[0].getArray().size() + 1) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations(array.size() + 1) : 1); } }, - sum(1) { + sum(1, new int[] { AI.ARRAY }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; double somme = 0; - for (AbstractLeekValue val : parameters[0].getArray()) { - somme += val.getDouble(leekIA); + for (var val : array) { + somme += ai.real(val.getValue()); } - return LeekValueManager.getLeekDoubleValue(somme); - } - - @Override - public int[] parameters() { - return new int[] { ARRAY }; + return somme; } @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? mVariableOperations.getOperations(parameters[0].getArray().size() + 1) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations(array.size() + 1) : 1); } }, - average(1) { + average(1, new int[] { AI.ARRAY }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; double average = 0; - for (AbstractLeekValue val : parameters[0].getArray()) { - average += val.getDouble(leekIA); + for (var val : array) { + average += ai.real(val.getValue()); } if (average == 0) - return LeekValueManager.getLeekIntValue(0); - return LeekValueManager.getLeekDoubleValue(average / parameters[0].getArray().size()); - } - - @Override - public int[] parameters() { - return new int[] { ARRAY }; + return 0.0; + return average / array.size(); } @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? mVariableOperations.getOperations(parameters[0].getArray().size() + 1) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations(array.size() + 1) : 1); } }, - fill(2, 3) { + fill(2, 3, new int[] { AI.ARRAY, -1, -1 }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - ArrayLeekValue array = parameters[0].getArray(); + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; int size = array.size(); - if (isType(parameters[2], NUMBER)) - size = parameters[2].getInt(leekIA); - AbstractLeekValue copy = LeekOperations.clone(leekIA, parameters[1].getValue()); + if (AI.isType(parameters[2], AI.NUMBER)) + size = ai.integer(parameters[2]); for (int i = 0; i < size; i++) { - array.get(leekIA, i).set(leekIA, copy); - leekIA.addOperations(3); + array.put(ai, i, parameters[1]); + ai.ops(3); } - return LeekValueManager.NULL; + return null; } @Override - public int[] parameters() { - return new int[] { ARRAY, -1, -1 }; - } - - @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception {} + public void addOperations(AI leekIA, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException {} }, - isEmpty(1) { + isEmpty(1, new int[] { AI.ARRAY }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekBooleanValue(parameters[0].getArray().size() == 0); - } - - @Override - public int[] parameters() { - return new int[] { ARRAY }; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + return array.size() == 0; } }, - subArray(3) { + subArray(3, new int[] { AI.ARRAY, AI.NUMBER, AI.NUMBER }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - ArrayLeekValue array = parameters[0].getArray(); - int start = parameters[1].getInt(leekIA); - int end = parameters[2].getInt(leekIA); + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + int start = ai.integer(parameters[1]); + int end = ai.integer(parameters[2]); if (start < 0 || end < start || end >= array.size()) - return LeekValueManager.NULL; + return null; ArrayLeekValue retour = new ArrayLeekValue(); int i = 0; - for (AbstractLeekValue val : array) { + for (var val : array) { if (i >= start && i <= end) { - retour.push(leekIA, LeekOperations.clone(leekIA, val.getValue())); - leekIA.addOperations(1); + retour.push(ai, LeekOperations.clone(ai, val.getValue())); + ai.ops(1); } i++; } @@ -1014,331 +848,327 @@ public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValu } @Override - public int[] parameters() { - return new int[] { ARRAY, NUMBER, NUMBER }; - } - - @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception {} + public void addOperations(AI leekIA, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException {} }, - pushAll(2) { + pushAll(2, new int[] { AI.ARRAY, AI.ARRAY }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - ArrayLeekValue array = parameters[0].getArray(); - ArrayLeekValue source = LeekOperations.clone(leekIA, parameters[1]).getArray(); - for (AbstractLeekValue value : source) { - array.push(leekIA, value.getValue()); - leekIA.addOperations(1); + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + var array2 = (ArrayLeekValue) parameters[1]; + for (var value : array2) { + if (leekIA.getVersion() == 1) { + array.push(leekIA, LeekOperations.clone(leekIA, value.getValue())); + } else { + array.push(leekIA, value.getValue()); + } + leekIA.ops(1); } - return LeekValueManager.NULL; + return null; } @Override - public int[] parameters() { - return new int[] { ARRAY, ARRAY }; - } - - @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception {} + public void addOperations(AI leekIA, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException {} }, - assocReverse(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - parameters[0].getArray().assocReverse(); - return LeekValueManager.NULL; - } - + assocReverse(1, new int[] { AI.ARRAY }) { @Override - public int[] parameters() { - return new int[] { ARRAY }; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + array.assocReverse(); + return null; } @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? mVariableOperations.getOperations(parameters[0].getArray().size() + 1) : 1); + public void addOperations(AI ai, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + ai.ops(hasVariableOperations() ? mVariableOperations.getOperations(((ArrayLeekValue) parameters[0]).size() + 1) : 1); } }, - arrayMap(2) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return leekIA.arrayMap(parameters[0].getArray(), parameters[1]); - } - + arrayMap(2, new int[] { AI.ARRAY, AI.FUNCTION }) { @Override - public int[] parameters() { - return new int[] { ARRAY, FUNCTION }; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + var fun = (FunctionLeekValue) parameters[1]; + if (leekIA.getVersion() >= 2) { + return leekIA.arrayMap(array, fun); + } else { + return leekIA.arrayMapV1(array, fun); + } } }, - arrayFilter(2) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return leekIA.arrayFilter(parameters[0].getArray(), parameters[1]); - } - + arrayFilter(2, new int[] { AI.ARRAY, AI.FUNCTION }) { @Override - public int[] parameters() { - return new int[] { ARRAY, FUNCTION }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + if (ai.getVersion() >= 2) { + return ai.arrayFilter((ArrayLeekValue) parameters[0], (FunctionLeekValue) parameters[1]); + } else { + return ai.arrayFilterV1((ArrayLeekValue) parameters[0], (FunctionLeekValue) parameters[1]); + } } }, - arrayFlatten(1, 2) { + arrayFlatten(1, 2, new int[] { AI.ARRAY, -1 }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - int maxDepth = isType(parameters[1], NUMBER) ? parameters[1].getInt(leekIA) : 1; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + int maxDepth = AI.isType(parameters[1], AI.NUMBER) ? ai.integer(parameters[1]) : 1; ArrayLeekValue retour = new ArrayLeekValue(); - leekIA.arrayFlatten(parameters[0].getArray(), retour, maxDepth); + ai.arrayFlatten((ArrayLeekValue) parameters[0], retour, maxDepth); return retour; } @Override - public int[] parameters() { - return new int[] { ARRAY, -1 }; - } - - @Override - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(hasVariableOperations() ? mVariableOperations.getOperations(retour.getArray().size() + 1) : 1); + public void addOperations(AI leekIA, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + leekIA.ops(hasVariableOperations() ? mVariableOperations.getOperations(((ArrayLeekValue) retour).size() + 1) : 1); } }, - arrayFoldLeft(2, 3) { + arrayFoldLeft(2, 3, new int[] { AI.ARRAY, AI.FUNCTION, -1 }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return leekIA.arrayFoldLeft(parameters[0].getArray(), parameters[1], parameters[2]); - } - - @Override - public int[] parameters() { - return new int[] { ARRAY, FUNCTION, -1 }; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + return leekIA.arrayFoldLeft((ArrayLeekValue) parameters[0], (FunctionLeekValue) parameters[1], parameters[2]); } }, - arrayFoldRight(2, 3) { + arrayFoldRight(2, 3, new int[] { AI.ARRAY, AI.FUNCTION, -1 }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return leekIA.arrayFoldRight(parameters[0].getArray(), parameters[1], parameters[2]); - } - - @Override - public int[] parameters() { - return new int[] { ARRAY, FUNCTION, -1 }; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var array = (ArrayLeekValue) parameters[0]; + return leekIA.arrayFoldRight(array, (FunctionLeekValue) parameters[1], parameters[2]); } }, - arrayPartition(2) { + arrayPartition(2, new int[] { AI.ARRAY, AI.FUNCTION }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return leekIA.arrayPartition(parameters[0].getArray(), parameters[1]); - } - - @Override - public int[] parameters() { - return new int[] { ARRAY, FUNCTION }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + if (ai.getVersion() >= 2) { + return ai.arrayPartition((ArrayLeekValue) parameters[0], (FunctionLeekValue) parameters[1]); + } else { + return ai.arrayPartitionV1((ArrayLeekValue) parameters[0], (FunctionLeekValue) parameters[1]); + } } }, - arrayIter(2) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return leekIA.arrayIter(parameters[0].getArray(), parameters[1]); - } - + arrayIter(2, new int[] { AI.ARRAY, AI.FUNCTION }) { @Override - public int[] parameters() { - return new int[] { ARRAY, FUNCTION }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + if (ai.getVersion() >= 2) { + return ai.arrayIter((ArrayLeekValue) parameters[0], (FunctionLeekValue) parameters[1]); + } else { + return ai.arrayIterV1((ArrayLeekValue) parameters[0], (FunctionLeekValue) parameters[1]); + } } }, - arrayConcat(2) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekOperations.add(leekIA, parameters[0], parameters[1]); - } - + arrayConcat(2, new int[] { AI.ARRAY, AI.ARRAY }) { @Override - public int[] parameters() { - return new int[] { ARRAY, ARRAY }; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + leekIA.ops(1); + return leekIA.add(parameters[0], parameters[1]); } }, - arraySort(2) { + arraySort(2, new int[] { AI.ARRAY, AI.FUNCTION }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return leekIA.arraySort(parameters[0].getArray(), parameters[1]); - } - - @Override - public int[] parameters() { - return new int[] { ARRAY, FUNCTION }; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + return leekIA.arraySort((ArrayLeekValue) parameters[0], (FunctionLeekValue) parameters[1]); } }, + + // debug(new CallableVersion[] { new CallableVersion(Type.NULL, new Type[] { Type.ANY }) }), + // debugW(new CallableVersion[] { new CallableVersion(Type.NULL, new Type[] { Type.ANY }) }), + // debugE(new CallableVersion[] { new CallableVersion(Type.NULL, new Type[] { Type.ANY }) }), debug(1) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - String p = parameters[0].getString(leekIA); - leekIA.getLogs().addLog(AILog.STANDARD, p); - leekIA.addOperations(p.length()); - return LeekValueManager.NULL; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + String message = LeekValueManager.getString(ai, parameters[0]); + ai.getLogs().addLog(AILog.STANDARD, message); + ai.ops(message.length()); + return null; } }, debugW(1) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - String p = parameters[0].getString(leekIA); - leekIA.getLogs().addLog(AILog.WARNING, p); - leekIA.addOperations(p.length()); - return LeekValueManager.NULL; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + String message = LeekValueManager.getString(ai, parameters[0]); + ai.getLogs().addLog(AILog.WARNING, message); + ai.ops(message.length()); + return null; } }, debugE(1) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - String p = parameters[0].getString(leekIA); - leekIA.getLogs().addLog(AILog.ERROR, p); - leekIA.addOperations(p.length()); - return LeekValueManager.NULL; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + String message = LeekValueManager.getString(ai, parameters[0]); + ai.getLogs().addLog(AILog.ERROR, message); + ai.ops(message.length()); + return null; } }, + debugC(2) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - String message = parameters[0].getString(leekIA); - int color = parameters[1].getInt(leekIA); - leekIA.getLogs().addLog(AILog.STANDARD, message, color); - leekIA.addOperations(message.length()); - return LeekValueManager.NULL; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + String message = LeekValueManager.getString(ai, parameters[0]); + int color = ai.integer(parameters[1]); + ai.getLogs().addLog(AILog.STANDARD, message, color); + ai.ops(message.length()); + return null; } }, jsonEncode(1) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { return leekIA.jsonEncode(leekIA, parameters[0]); } }, - jsonDecode(1) { + jsonDecode(1, new int[] { AI.STRING }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return leekIA.jsonDecode(parameters[0].getString(leekIA)); - } - - @Override - public int[] parameters() { - return new int[] { STRING }; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + return leekIA.jsonDecode((String) parameters[0]); } }, getInstructionsCount(0) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekIntValue(0); + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + return 0; } }, color(3) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - leekIA.addOperations(AI.ERROR_LOG_COST); - leekIA.addSystemLog(AILog.WARNING, AILog.DEPRECATED_FUNCTION, new String[] { "color", "getColor" }); + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + leekIA.addSystemLog(AILog.WARNING, Error.DEPRECATED_FUNCTION, new String[] { "color", "getColor" }); return leekIA.color(parameters[0], parameters[1], parameters[2]); } }, getColor(3) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { return leekIA.color(parameters[0], parameters[1], parameters[2]); } }, - getRed(1) { - @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekIntValue(((parameters[0].getInt(leekIA)) >> 16) & 255); - } - + getRed(1, new int[] { AI.NUMBER }) { @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + return ((((Number) parameters[0]).intValue()) >> 16) & 255; } }, - getGreen(1) { + getGreen(1, new int[] { AI.NUMBER }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekIntValue(((parameters[0].getInt(leekIA)) >> 8) & 255); - } - - @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + return ((((Number) parameters[0]).intValue()) >> 8) & 255; } }, - getBlue(1) { + getBlue(1, new int[] { AI.NUMBER }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekIntValue(parameters[0].getInt(leekIA) & 255); - } - - @Override - public int[] parameters() { - return new int[] { NUMBER }; + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + return ((Number) parameters[0]).intValue() & 255; } }, - + typeOf(1) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekIntValue(leekIA.typeOf(parameters[0])); + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + return LeekValueManager.getType(parameters[0]); } }, - trim(1) { + trim(1, new int[] { AI.STRING }) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return new StringLeekValue(parameters[0].getString(leekIA).trim()); + public Object run(AI leekIA, ILeekFunction function, Object... parameters) throws LeekRunException { + var s = (String) parameters[0]; + return s.trim(); } + }, + // getOperations(new CallableVersion[] { new CallableVersion(Type.INT, new Type[0]) }), + getOperations(0) { @Override - public int[] parameters() { - return new int[] { STRING }; + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + return ai.getOperations(); } }, - getOperations(0) { + + clone(1, 2) { @Override - public AbstractLeekValue run(AI leekIA, ILeekFunction function, AbstractLeekValue[] parameters, int count) throws Exception { - return LeekValueManager.getLeekIntValue((int) leekIA.getOperations()); + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { + // Clone one level by default + int level = parameters[1] == null ? 1 : Math.max(0, ai.integer(parameters[1])); + return LeekOperations.clone(ai, parameters[0], level); } - }; - + } + ; + private static String extraFunctions = null; private int mArguments; private int mArgumentsMin; - private Integer mOperations = null; + private int mOperations = 1; protected VariableOperations mVariableOperations = null; - - public static final int DOUBLE = 1; - public static final int INT = 2; - public static final int BOOLEAN = 3; - public static final int STRING = 4; - public static final int NULL = 5; - public static final int ARRAY = 6; - public static final int NUMBER = 7; - public static final int FUNCTION = 8; + private int[] parameters; + private Type return_type; + private CallableVersion[] versions; + private boolean direct = false; LeekFunctions(int arguments) { mArgumentsMin = arguments; mArguments = arguments; + // this.parameters = new int[0]; + } + + LeekFunctions(int arguments, int[] parameters) { + mArgumentsMin = arguments; + mArguments = arguments; + this.parameters = parameters; + } + + LeekFunctions(int arguments, int arguments_max, int[] parameters) { + mArgumentsMin = arguments; + mArguments = arguments_max; + this.parameters = parameters; } LeekFunctions(int arguments, int arguments_max) { mArgumentsMin = arguments; mArguments = arguments_max; + // this.parameters = new int[0]; + } + + LeekFunctions(CallableVersion[] versions) { + this.versions = versions; + this.direct = true; + // this.parameters = new int[0]; + mArguments = this.versions[0].arguments.length; + } + + LeekFunctions(Type return_type, CallableVersion[] versions, int[] parameters) { + this.return_type = return_type; + this.versions = versions; + this.direct = true; + this.parameters = parameters; + } + + public boolean isDirect() { + return direct; + } + + public int[] getParameters() { + return parameters; + } + + public Type getReturnType() { + return return_type; + } + + public CallableVersion[] getVersions() { + return versions; } - + public static void setExtraFunctions(String extraFunctions) { LeekFunctions.extraFunctions = extraFunctions; } - + public static int isFunction(String name) { ILeekFunction f = getValue(name); if (f == null) return -1; return f.getArguments(); } - + public static boolean isExtraFunction(String name) { ILeekFunction f = getValue(name); return f != null && f.isExtra(); } - + public static String getNamespace(String name) { return isExtraFunction(name) ? extraFunctions : "LeekFunctions"; } @@ -1353,27 +1183,18 @@ public String getNamespace() { return "LeekFunctions"; } - + @Override public int getArgumentsMin() { return mArgumentsMin; } - + @Override public boolean isExtra() { return false; } public int getOperations() { - if (mOperations == null) { - if (!Functions.isReady()) { - return 0; - } - mOperations = Functions.getOperations(this.name()); - if (mOperations <= 0) { - mOperations = 1; - } - } return mOperations; } @@ -1385,10 +1206,9 @@ public boolean hasVariableOperations() { } public static ILeekFunction getValue(String name) { - for (LeekFunctions func : LeekFunctions.values()) { - if (func.name().equals(name)) - return func; - } + try { + return LeekFunctions.valueOf(name); + } catch (Exception e) {} if (extraFunctions != null) { try { Class extra = Class.forName(extraFunctions); @@ -1400,11 +1220,11 @@ public static ILeekFunction getValue(String name) { } catch (ClassNotFoundException e) { e.printStackTrace(); } - + } return null; } - + public static Object[] getExtraFunctions() { if (extraFunctions != null) { try { @@ -1413,7 +1233,7 @@ public static Object[] getExtraFunctions() { } catch (ClassNotFoundException e) { return new Object[] {}; } - + } return new Object[] {}; } @@ -1421,9 +1241,7 @@ public static Object[] getExtraFunctions() { /* * Lancer la fonction */ - public abstract AbstractLeekValue run(AI ai, ILeekFunction function, AbstractLeekValue parameters[], int count) throws Exception; - - public int[] parameters() { + public Object run(AI ai, ILeekFunction function, Object... parameters) throws LeekRunException { return null; } @@ -1431,69 +1249,11 @@ public int cost() { return 1; } - public void addOperations(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], AbstractLeekValue retour, int count) throws Exception { - leekIA.addOperations(getOperations()); - } - - public static AbstractLeekValue executeFunction(AI leekIA, ILeekFunction function, AbstractLeekValue parameters[], int count) throws Exception { - - // Vérification parametres - int[] types = function.parameters(); - if (types == null || verifyParameters(types, parameters)) { - AbstractLeekValue retour = function.run(leekIA, function, parameters, count); - function.addOperations(leekIA, function, parameters, retour, count); - return retour; - } else { - // Message d'erreur - String ret = AbstractLeekValue.getParamString(parameters); - leekIA.addOperations(AI.ERROR_LOG_COST); - leekIA.addSystemLog(AILog.ERROR, AILog.UNKNOWN_FUNCTION, new String[] { function + "(" + ret + ")" }); - return LeekValueManager.NULL; - } - // throw new LeekRunException(LeekRunException.UNKNOWN_FUNCTION); - } - - public static int intOrNull(AI ai, AbstractLeekValue value) throws LeekRunException { - if (isType(value, NULL)) - return -1; - return value.getInt(ai); - } - - public static boolean verifyParameters(int[] types, AbstractLeekValue[] parameters) { - if (parameters == null) { - return types.length == 0; - } - if (types.length != parameters.length) { - return false; - } - for (int i = 0; i < types.length; i++) { - if (types[i] == -1) { - continue; - } - if (!isType(parameters[i], types[i])) { - return false; - } - } - return true; + public void setOperations(int operations) { + mOperations = operations; } - public static boolean isType(AbstractLeekValue value, int type) { - if (type == BOOLEAN && !(value instanceof BooleanLeekValue)) - return false; - if (type == INT && !(value instanceof IntLeekValue)) - return false; - if (type == DOUBLE && !(value instanceof DoubleLeekValue)) - return false; - if (type == STRING && !(value instanceof StringLeekValue)) - return false; - if (type == NULL && !(value instanceof NullLeekValue)) - return false; - if (type == ARRAY && !(value instanceof ArrayLeekValue)) - return false; - if (type == FUNCTION && !(value instanceof FunctionLeekValue)) - return false; - if (type == NUMBER && !(value instanceof IntLeekValue) && !(value instanceof DoubleLeekValue)) - return false; - return true; + public void addOperations(AI leekIA, ILeekFunction function, Object parameters[], Object retour) throws LeekRunException { + leekIA.ops(getOperations()); } } diff --git a/src/main/java/leekscript/runner/LeekOperations.java b/src/main/java/leekscript/runner/LeekOperations.java index a3e1e365..6bf2126b 100644 --- a/src/main/java/leekscript/runner/LeekOperations.java +++ b/src/main/java/leekscript/runner/LeekOperations.java @@ -1,273 +1,82 @@ package leekscript.runner; -import leekscript.AILog; -import leekscript.runner.values.AbstractLeekValue; import leekscript.runner.values.ArrayLeekValue; -import leekscript.runner.values.ArrayLeekValue.ArrayIterator; -import leekscript.runner.values.BooleanLeekValue; -import leekscript.runner.values.DoubleLeekValue; -import leekscript.runner.values.FunctionLeekValue; -import leekscript.runner.values.IntLeekValue; -import leekscript.runner.values.StringLeekValue; +import leekscript.runner.values.ObjectLeekValue; public class LeekOperations { - public static AbstractLeekValue add(AI ai, AbstractLeekValue v1, - AbstractLeekValue v2) throws Exception { - - v1 = v1.getValue(); - v2 = v2.getValue(); - - if (v1.isNumeric() && v2.isNumeric()) { - ai.addOperations(1); - if (v1 instanceof DoubleLeekValue || v2 instanceof DoubleLeekValue) { - return LeekValueManager.getLeekDoubleValue(v1.getDouble(ai) + v2.getDouble(ai)); - } else { - return LeekValueManager.getLeekIntValue(v1.getInt(ai) + v2.getInt(ai)); - } - } - - // Concatenate arrays - if (v1 instanceof ArrayLeekValue && v2 instanceof ArrayLeekValue) { - - ai.addOperations(1 + (v1.getArray().size() + v2.getArray().size()) * 2); - - ArrayLeekValue retour = new ArrayLeekValue(); - ArrayIterator iterator = v1.getArray().getArrayIterator(); - - while (!iterator.ended()) { - if (iterator.key() instanceof String) { - retour.getOrCreate(ai, iterator.getKey(ai)).set(ai, iterator.getValue(ai)); - } else { - retour.push(ai, iterator.getValue(ai)); - } - iterator.next(); - } - iterator = v2.getArray().getArrayIterator(); - while (!iterator.ended()) { - if (iterator.key() instanceof String) { - retour.getOrCreate(ai, iterator.getKey(ai)).set(ai, iterator.getValue(ai)); - } else { - retour.push(ai, iterator.getValue(ai)); - } - iterator.next(); - } - return retour; - } - - String v1_string = v1.getString(ai); - String v2_string = v2.getString(ai); - ai.addOperations(1 + v1_string.length() + v2_string.length()); - return new StringLeekValue(v1_string + v2_string); + public static Object and(AI ai, Object v1, Object v2) throws LeekRunException { + // ai.ops(1); + return ai.bool(v1) && ai.bool(v2); } - public static AbstractLeekValue minus(AI ai, AbstractLeekValue v1, AbstractLeekValue v2) throws Exception { - ai.addOperations(1); - v1 = v1.getValue(); - v2 = v2.getValue(); - if (v1.isNumeric() && v2.isNumeric()) { - if (v1 instanceof DoubleLeekValue || v2 instanceof DoubleLeekValue) - return LeekValueManager.getLeekDoubleValue(v1.getDouble(ai) - v2.getDouble(ai)); - else - return LeekValueManager.getLeekIntValue(v1.getInt(ai) - v2.getInt(ai)); - } - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); + public static Object or(AI ai, Object v1, Object v2) throws LeekRunException { + // ai.ops(1); + return ai.bool(v1) || ai.bool(v2); } - public static AbstractLeekValue power(AI ai, AbstractLeekValue v1, AbstractLeekValue v2) throws Exception { - ai.addOperations(AbstractLeekValue.POW_COST); - v1 = v1.getValue(); - v2 = v2.getValue(); - if (v1.isNumeric() && v2.isNumeric()) { - if (v1 instanceof DoubleLeekValue || v2 instanceof DoubleLeekValue) { - double result = Math.pow(v1.getDouble(ai), v2.getDouble(ai)); - if (Double.isNaN(result)) - return LeekValueManager.NULL; - return LeekValueManager.getLeekDoubleValue(result); - } else { - double result = Math.pow(v1.getInt(ai), v2.getInt(ai)); - if (Double.isNaN(result)) - return LeekValueManager.NULL; - return LeekValueManager.getLeekIntValue((int) result); - } - } - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); + public static int band(AI ai, Object v1, Object v2) throws LeekRunException { + // ai.ops(1); + return ai.integer(v1) & ai.integer(v2); } - public static AbstractLeekValue multiply(AI ai, AbstractLeekValue v1, AbstractLeekValue v2) throws Exception { - ai.addOperations(AbstractLeekValue.MUL_COST); - v1 = v1.getValue(); - v2 = v2.getValue(); - if (v1.isNumeric() && v2.isNumeric()) { - if (v1 instanceof DoubleLeekValue || v2 instanceof DoubleLeekValue) { - return LeekValueManager.getLeekDoubleValue(v1.getDouble(ai) * v2.getDouble(ai)); - } else { - return LeekValueManager.getLeekIntValue(v1.getInt(ai) * v2.getInt(ai)); - } - } - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); + public static int bleft(AI ai, Object v1, Object v2) throws LeekRunException { + // ai.ops(1); + return ai.integer(v1) << ai.integer(v2); } - public static AbstractLeekValue divide(AI ai, AbstractLeekValue v1, AbstractLeekValue v2) throws Exception { - - ai.addOperations(AbstractLeekValue.DIV_COST); - - v1 = v1.getValue(); - v2 = v2.getValue(); - if (v1.isNumeric() && v2.isNumeric()) { - if (v2.getDouble(ai) == 0) { - ai.addOperations(AI.ERROR_LOG_COST); - ai.addSystemLog(AILog.ERROR, AILog.DIVISION_BY_ZERO); - return LeekValueManager.NULL; - } - if (v1 instanceof DoubleLeekValue || v2 instanceof DoubleLeekValue) { - return LeekValueManager.getLeekDoubleValue(v1.getDouble(ai) / v2.getDouble(ai)); - } else { - if (v1.getInt(ai) % v2.getInt(ai) != 0) - return new DoubleLeekValue(v1.getDouble(ai) / v2.getDouble(ai)); - else - return LeekValueManager.getLeekIntValue(v1.getInt(ai) / v2.getInt(ai)); - } - } - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); - } - - public static AbstractLeekValue modulus(AI ai, AbstractLeekValue v1, AbstractLeekValue v2) throws Exception { - - ai.addOperations(AbstractLeekValue.MOD_COST); - v1 = v1.getValue(); - v2 = v2.getValue(); - if (v1.isNumeric() && v2.isNumeric()) { - if (v2.getDouble(ai) == 0) { - ai.addOperations(AI.ERROR_LOG_COST); - ai.addSystemLog(AILog.ERROR, AILog.DIVISION_BY_ZERO); - return LeekValueManager.NULL; - } - if (v1 instanceof DoubleLeekValue || v2 instanceof DoubleLeekValue) - return LeekValueManager.getLeekDoubleValue(v1.getDouble(ai) % v2.getDouble(ai)); - else - return LeekValueManager.getLeekIntValue(v1.getInt(ai) % v2.getInt(ai)); - } - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); + public static int bright(AI ai, Object v1, Object v2) throws LeekRunException { + // ai.ops(1); + return ai.integer(v1) >> ai.integer(v2); } - public static AbstractLeekValue and(AI ai, AbstractLeekValue v1, - AbstractLeekValue v2) throws Exception { - ai.addOperations(1); - return LeekValueManager.getLeekBooleanValue(v1.getBoolean() && v2.getBoolean()); + public static int buright(AI ai, Object v1, Object v2) throws LeekRunException { + // ai.ops(1); + return ai.integer(v1) >>> ai.integer(v2); } - public static AbstractLeekValue or(AI ai, AbstractLeekValue v1, - AbstractLeekValue v2) throws Exception { - ai.addOperations(1); - return LeekValueManager.getLeekBooleanValue(v1.getBoolean() || v2.getBoolean()); + public static boolean equals(AI ai, Object v1, Object v2) throws LeekRunException { + // ai.ops(1); + return ai.eq(v1, v2); } - public static AbstractLeekValue bor(AI ai, AbstractLeekValue v1, - AbstractLeekValue v2) throws Exception { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(v1.getInt(ai) | v2.getInt(ai)); + public static boolean notequals(AI ai, Object v1, Object v2) throws LeekRunException { + // ai.ops(1); + return !ai.eq(v1, v2); } - public static AbstractLeekValue band(AI ai, AbstractLeekValue v1, - AbstractLeekValue v2) throws Exception { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(v1.getInt(ai) & v2.getInt(ai)); + public static Object clone(AI ai, Object value) throws LeekRunException { + return clone(ai, value, 1); } - public static AbstractLeekValue bxor(AI ai, AbstractLeekValue v1, - AbstractLeekValue v2) throws Exception { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(v1.getInt(ai) ^ v2.getInt(ai)); - } - - public static AbstractLeekValue bleft(AI ai, AbstractLeekValue v1, - AbstractLeekValue v2) throws Exception { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(v1.getInt(ai) << v2.getInt(ai)); - } - - public static AbstractLeekValue bright(AI ai, AbstractLeekValue v1, - AbstractLeekValue v2) throws Exception { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(v1.getInt(ai) >> v2.getInt(ai)); - } - - public static AbstractLeekValue brotate(AI ai, AbstractLeekValue v1, - AbstractLeekValue v2) throws Exception { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(v1.getInt(ai) >>> v2.getInt(ai)); - } - - public static AbstractLeekValue equals(AI ai, AbstractLeekValue v1, - AbstractLeekValue v2) throws Exception { - ai.addOperations(1); - return LeekValueManager.getLeekBooleanValue(v1.equals(ai, v2)); - } - - public static AbstractLeekValue notequals(AI ai, AbstractLeekValue v1, - AbstractLeekValue v2) throws Exception { - ai.addOperations(1); - return LeekValueManager.getLeekBooleanValue(v1.notequals(ai, v2)); - } - - public static AbstractLeekValue less(AI ai, AbstractLeekValue v1, - AbstractLeekValue v2) throws Exception { - ai.addOperations(1); - return LeekValueManager.getLeekBooleanValue(v1.less(ai, v2)); - } - - public static AbstractLeekValue more(AI ai, AbstractLeekValue v1, - AbstractLeekValue v2) throws Exception { - ai.addOperations(1); - return LeekValueManager.getLeekBooleanValue(v1.more(ai, v2)); - } - - public static AbstractLeekValue lessequals(AI ai, AbstractLeekValue v1, - AbstractLeekValue v2) throws Exception { - ai.addOperations(1); - return LeekValueManager.getLeekBooleanValue(v1.lessequals(ai, v2)); - } - - public static AbstractLeekValue moreequals(AI ai, AbstractLeekValue v1, - AbstractLeekValue v2) throws Exception { - ai.addOperations(1); - return LeekValueManager.getLeekBooleanValue(v1.moreequals(ai, v2)); - } - - public static AbstractLeekValue clone(AI ai, AbstractLeekValue value) - throws Exception { - value = value.getValue(); - ai.addOperations(1); - if (value instanceof StringLeekValue) - return new StringLeekValue(value.getString(ai)); - else if (value instanceof FunctionLeekValue) - return ((FunctionLeekValue) value).cloneFunction(); - else if (value instanceof BooleanLeekValue) - return LeekValueManager.getLeekBooleanValue(value.getBoolean()); - else if (value instanceof IntLeekValue) - return LeekValueManager.getLeekIntValue(value.getInt(ai)); - else if (value instanceof DoubleLeekValue) - return new DoubleLeekValue(value.getDouble(ai)); - else if (value instanceof ArrayLeekValue) { - if (value.getArray().size() > 0) { - ai.addOperations(value.getArray().size() * (ArrayLeekValue.ARRAY_CELL_CREATE_OPERATIONS)); + public static Object clone(AI ai, Object value, int level) throws LeekRunException { + if (value instanceof ArrayLeekValue) { + if (level == 0) return value; + // System.out.println("ops Clone Array begin"); + ai.ops(1); + var array = (ArrayLeekValue) value; + if (array.size() > 0) { + // System.out.println("ops Clone Array"); + ai.ops(array.size() * (ArrayLeekValue.ARRAY_CELL_CREATE_OPERATIONS)); } - return new ArrayLeekValue(ai, value.getArray()); - } else - return LeekValueManager.NULL; + return new ArrayLeekValue(ai, array, level); + } else if (value instanceof ObjectLeekValue) { + if (level == 0) return value; + ai.ops(1); + return new ObjectLeekValue(ai, (ObjectLeekValue) value, level); + } + return value; } - public static AbstractLeekValue equals_equals(AI ai, AbstractLeekValue v1, - AbstractLeekValue v2) throws Exception { - ai.addOperations(1); - return LeekValueManager.getLeekBooleanValue(v1.getType() == v2.getType() && v1.equals(ai, v2)); + public static boolean equals_equals(AI ai, Object v1, Object v2) throws LeekRunException { + ai.ops(1); + if (v1 instanceof ObjectLeekValue && v2 instanceof ObjectLeekValue) { + return v1 == v2; + } + return LeekValueManager.getType(v1) == LeekValueManager.getType(v2) && ai.eq(v1, v2); } - public static AbstractLeekValue notequals_equals(AI ai, AbstractLeekValue v1, - AbstractLeekValue v2) throws Exception { - ai.addOperations(1); - return LeekValueManager.getLeekBooleanValue(v1.getType() != v2.getType() - || v1.notequals(ai, v2)); + public static boolean notequals_equals(AI ai, Object v1, Object v2) throws LeekRunException { + return !equals_equals(ai, v1, v2); } } diff --git a/src/main/java/leekscript/runner/LeekRunException.java b/src/main/java/leekscript/runner/LeekRunException.java index e43ffb17..36757e5c 100644 --- a/src/main/java/leekscript/runner/LeekRunException.java +++ b/src/main/java/leekscript/runner/LeekRunException.java @@ -10,9 +10,9 @@ public class LeekRunException extends Exception { public final static int ARRAY_EMPTY = 2; public final static int INVALID_INDEX = 3; public final static int UNKNOWN_FUNCTION = 4; - public final static int INVALID_OPERATOR = 5; public final static int INVALID_LEVEL = 6; public final static int OUT_OF_MEMORY = 7; + public final static int UNKNOWN_FIELD = 8; public LeekRunException(int error) { mError = error; @@ -30,21 +30,20 @@ public int getError() { public String getMessage() { switch (mError) { case TOO_MUCH_OPERATIONS: - return "Erreur d'execution : Trop d'opérations éxécutées pour ce tour"; + return "Erreur d'exécution : Trop d'opérations exécutées pour ce tour"; case ARRAY_EMPTY: - return "Erreur d'execution : Tableau vide"; + return "Erreur d'exécution : Tableau vide"; case INVALID_INDEX: - return "Erreur d'execution : Indice invalide"; + return "Erreur d'exécution : Indice invalide"; case UNKNOWN_FUNCTION: - return "Erreur d'execution : Fonction inconnue"; - case INVALID_OPERATOR: - return "Erreur d'execution : Impossible d'utiliser cet opérateur"; + return "Erreur d'exécution : Fonction inconnue"; case INVALID_LEVEL: - return "Erreur d'execution : Niveau invalide"; + return "Erreur d'exécution : Niveau invalide"; case OUT_OF_MEMORY: - return "Erreur d'execution : Trop de RAM utilisée"; - + return "Erreur d'exécution : Trop de RAM utilisée"; + case UNKNOWN_FIELD: + return "Erreur d'exécution : Champ inconnu"; } - return "Erreur d'éxécution"; + return "Erreur d'exécution"; } } diff --git a/src/main/java/leekscript/runner/LeekValueComparator.java b/src/main/java/leekscript/runner/LeekValueComparator.java index 1073af7d..b80f88c3 100644 --- a/src/main/java/leekscript/runner/LeekValueComparator.java +++ b/src/main/java/leekscript/runner/LeekValueComparator.java @@ -2,25 +2,25 @@ import java.util.Comparator; -import leekscript.runner.values.AbstractLeekValue; +import leekscript.runner.values.ArrayLeekValue; public class LeekValueComparator { - public static class SortComparator implements Comparator { + public static class SortComparator implements Comparator { private final int mOrder; - private final AI ai; + // private final AI ai; public final static int SORT_ASC = 1; public final static int SORT_DESC = 2; public SortComparator(AI ai, int order) { mOrder = order; - this.ai = ai; + // this.ai = ai; } @Override - public int compare(AbstractLeekValue v1, AbstractLeekValue v2) { + public int compare(Object v1, Object v2) { try { if (mOrder == SORT_ASC) return compareAsc(v1, v2); @@ -32,50 +32,55 @@ else if (mOrder == SORT_DESC) return 0; } - public int compareAsc(AbstractLeekValue v1, AbstractLeekValue v2) throws LeekRunException { - if (LeekFunctions.isType(v1, LeekFunctions.BOOLEAN)) { - if (LeekFunctions.isType(v2, LeekFunctions.BOOLEAN)) { - if (v1.getBoolean() == v2.getBoolean()) + public int compareAsc(Object v1, Object v2) throws LeekRunException { + if (v1 == null) { + if (v2 == null) return 0; + return -1; + } else if (v1 instanceof Boolean) { + if (v2 == null) return 1; + if (v2 instanceof Boolean) { + if ((Boolean) v1 == (Boolean) v2) return 0; - else if (v1.getBoolean()) + else if ((Boolean) v1) return 1; else return -1; } return -1; - } else if (LeekFunctions.isType(v1, LeekFunctions.NUMBER)) { - if (LeekFunctions.isType(v2, LeekFunctions.NUMBER)) { - if (v1.getDouble(ai) == v2.getDouble(ai)) + } else if (v1 instanceof Number) { + if (v2 instanceof Number) { + if (((Number) v1).doubleValue() == ((Number) v2).doubleValue()) return 0; - else if (v1.getDouble(ai) < v2.getDouble(ai)) + else if (((Number) v1).doubleValue() < ((Number) v2).doubleValue()) return -1; else return 1; - } else if (LeekFunctions.isType(v2, LeekFunctions.BOOLEAN)) + } else if (v2 instanceof Boolean || v2 == null) return 1; else return -1; - } else if (LeekFunctions.isType(v1, LeekFunctions.STRING)) { - if (LeekFunctions.isType(v2, LeekFunctions.STRING)) { - return v1.getString(ai).compareTo(v2.getString(ai)); - } else if (LeekFunctions.isType(v2, LeekFunctions.NUMBER) || LeekFunctions.isType(v2, LeekFunctions.BOOLEAN)) + } else if (v1 instanceof String) { + if (v2 instanceof String) { + return ((String) v1).compareTo((String) v2); + } else if (v2 instanceof Number || v2 instanceof Boolean || v2 == null) return 1; else return -1; - } else if (LeekFunctions.isType(v1, LeekFunctions.ARRAY)) { - if (LeekFunctions.isType(v2, LeekFunctions.ARRAY)) { - if (v1.getArray().size() == v2.getArray().size()) + } else if (v1 instanceof ArrayLeekValue) { + if (v2 instanceof ArrayLeekValue) { + if (((ArrayLeekValue) v1).size() == ((ArrayLeekValue) v2).size()) return 0; - else if (v1.getArray().size() < v2.getArray().size()) + else if (((ArrayLeekValue) v1).size() < ((ArrayLeekValue) v2).size()) return -1; else return 1; - } else if (LeekFunctions.isType(v2, LeekFunctions.NULL)) + } else if (v2 == null) return -1; else return 1; - } else + } else { return -1; + } } } } diff --git a/src/main/java/leekscript/runner/LeekValueManager.java b/src/main/java/leekscript/runner/LeekValueManager.java index 57cd699e..b66d798f 100644 --- a/src/main/java/leekscript/runner/LeekValueManager.java +++ b/src/main/java/leekscript/runner/LeekValueManager.java @@ -2,17 +2,19 @@ import java.math.BigDecimal; import java.math.BigInteger; +import java.text.DecimalFormat; +import java.util.HashSet; +import java.util.Set; import java.util.TreeMap; -import leekscript.ErrorManager; -import leekscript.runner.values.AbstractLeekValue; +import leekscript.AILog; import leekscript.runner.values.ArrayLeekValue; -import leekscript.runner.values.BooleanLeekValue; -import leekscript.runner.values.DoubleLeekValue; +import leekscript.runner.values.ClassLeekValue; import leekscript.runner.values.FunctionLeekValue; -import leekscript.runner.values.IntLeekValue; -import leekscript.runner.values.NullLeekValue; -import leekscript.runner.values.StringLeekValue; +import leekscript.runner.values.LeekValue; +import leekscript.runner.values.ObjectLeekValue; +import leekscript.runner.values.Box; +import leekscript.common.Error; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; @@ -23,24 +25,9 @@ public class LeekValueManager { init(); } - public final static NullLeekValue NULL = new NullLeekValue(); - public final static BooleanLeekValue TRUE = new BooleanLeekValue(true); - public final static BooleanLeekValue FALSE = new BooleanLeekValue(false); - public final static int MIN_INT = -50; - public final static int MAX_INT = 800; - - private static TreeMap mIntegers; private static TreeMap mFunctions; public static void init() { - mIntegers = new TreeMap(); - for (int i = MIN_INT; i <= MAX_INT; i++) { - try { - mIntegers.put(i, new IntLeekValue(i)); - } catch (Exception e) { - ErrorManager.exception(e); - } - } mFunctions = new TreeMap(); for (LeekFunctions function : LeekFunctions.values()) { mFunctions.put(function.toString(), new FunctionLeekValue(function)); @@ -54,57 +41,22 @@ public static FunctionLeekValue getFunction(ILeekFunction function) { return mFunctions.get(function.toString()); } - public static AbstractLeekValue getLeekDoubleValue(double nb) { - if (nb == 0) - return getLeekIntValue(0); - else if (nb == 1) - return getLeekIntValue(1); - else if (nb == -1) - return getLeekIntValue(-1); - else - return new DoubleLeekValue(nb); - } - - public static AbstractLeekValue getLeekIntValue(int nb) { - // Si c'est une valeur en cache on la retourne - if (MIN_INT <= nb && nb <= MAX_INT) { - return mIntegers.get(nb); - } - return new IntLeekValue(nb); - } - - public static AbstractLeekValue getLeekIntValue(AI ai, int nb, AbstractLeekValue mValue) throws LeekRunException { - // Si c'est une valeur en cache on la retourne - if (MIN_INT <= nb && nb <= MAX_INT) - return mIntegers.get(nb); - // Si l'ancienne valeur est une valeur en cache on la modifie pas - if (MIN_INT <= mValue.getInt(ai) && mValue.getInt(ai) <= MAX_INT) - return new IntLeekValue(nb); - // Sinon on modifie direct la valeur - mValue.setInt(nb); - return mValue; - } - - public static AbstractLeekValue getLeekBooleanValue(boolean b) { - return b ? TRUE : FALSE; - } - - public static AbstractLeekValue parseJSON(Object o, AI ai) throws Exception { - + public static Object parseJSON(Object o, AI ai) throws LeekRunException { if (o instanceof Boolean) { - return new BooleanLeekValue((Boolean) o); + return o; } if (o instanceof String) { - return new StringLeekValue((String) o); + return o; } if (o instanceof Integer) { - return new IntLeekValue((Integer) o); + return o; } if (o instanceof BigInteger) { - throw new Exception(); + ai.addSystemLog(AILog.ERROR, Error.INVALID_OPERATOR, new String[] { "jsonDecode(" + LeekValueManager.getString(ai, o) + ")" }); + return null; } if (o instanceof BigDecimal) { - return new DoubleLeekValue(((BigDecimal) o).doubleValue()); + return ((BigDecimal) o).doubleValue(); } if (o instanceof JSONArray) { JSONArray a = (JSONArray) o; @@ -118,11 +70,172 @@ public static AbstractLeekValue parseJSON(Object o, AI ai) throws Exception { JSONObject a = (JSONObject) o; ArrayLeekValue array = new ArrayLeekValue(); for (String key : a.keySet()) { - array.getOrCreate(ai, new StringLeekValue(key)).set(ai, parseJSON(a.get(key), ai)); + array.getOrCreate(ai, key).set(parseJSON(a.get(key), ai)); } return array; } - return new StringLeekValue("Class " + o.getClass().getSimpleName()); + return "Class " + o.getClass().getSimpleName(); + } + + public static Object getValue(Object value) { + if (value instanceof Box) { + return ((Box) value).getValue(); + } + return value; + } + + public static String doubleToString(AI ai, double value) throws LeekRunException { + ai.ops(3); + if (ai.getVersion() >= 2) { + return String.valueOf((Double) value); + } else { + // if (((Double) value) == ((Double) value).intValue()) { + // return String.valueOf(((Double) value).intValue()); + // } + DecimalFormat df = new DecimalFormat(); + df.setMinimumFractionDigits(0); + return df.format((Double) value); + } + } + + public static String getString(AI ai, Object value) throws LeekRunException { + if (value instanceof Double) { + return doubleToString(ai, (Double) value); + } else if (value instanceof Integer) { + ai.ops(3); + return String.valueOf((Integer) value); + } else if (value instanceof Boolean) { + return String.valueOf((Boolean) value); + } else if (value instanceof ObjectLeekValue) { + return ((ObjectLeekValue) value).getString(ai, new HashSet()); + } else if (value instanceof ArrayLeekValue) { + return ((ArrayLeekValue) value).getString(ai, new HashSet()); + } else if (value instanceof String) { + return (String) value; + } else if (value instanceof ClassLeekValue) { + return ((ClassLeekValue) value).getString(ai); + } else if (value instanceof FunctionLeekValue) { + return ((FunctionLeekValue) value).getString(ai); + } else if (value instanceof Box) { + return getString(ai, ((Box) value).getValue()); + } + return "null"; + } + + public static String getString(AI ai, Object value, Set visited) throws LeekRunException { + if (value instanceof Double) { + return doubleToString(ai, (Double) value); + } else if (value instanceof Integer) { + ai.ops(3); + return String.valueOf((Integer) value); + } else if (value instanceof Boolean) { + return String.valueOf((Boolean) value); + } else if (value instanceof ObjectLeekValue) { + return ((ObjectLeekValue) value).getString(ai, visited); + } else if (value instanceof ArrayLeekValue) { + return ((ArrayLeekValue) value).getString(ai, visited); + } else if (value instanceof String) { + return (String) value; + } else if (value instanceof ClassLeekValue) { + return ((ClassLeekValue) value).getString(ai); + } else if (value instanceof FunctionLeekValue) { + return ((FunctionLeekValue) value).getString(ai); + } else if (value instanceof Box) { + return getString(ai, ((Box) value).getValue()); + } + return "null"; + } + + public static int bnot(AI ai, Object value) throws LeekRunException { + return ~ai.integer(value); + } + + public static FunctionLeekValue getFunction(AI ai, Object value) throws LeekRunException { + var v = getValue(value); + if (v instanceof FunctionLeekValue) { + return (FunctionLeekValue) v; + } + // On ne peux pas exécuter ce type de variable + ai.addSystemLog(AILog.ERROR, Error.CAN_NOT_EXECUTE_VALUE, new String[] { getString(ai, value) }); + return null; + } + + public static Object execute(AI ai, Object value, Object... args) throws LeekRunException { + if (value instanceof FunctionLeekValue) { + return ((FunctionLeekValue) value).execute(ai, args); + } + // On ne peux pas exécuter ce type de variable + ai.addSystemLog(AILog.ERROR, Error.CAN_NOT_EXECUTE_VALUE, new String[] { getString(ai, value) }); + return null; + } + + public static Box getOrCreate(AI ai, Object value, Object index) throws LeekRunException { + if (value instanceof ArrayLeekValue) { + return ((ArrayLeekValue) value).getOrCreate(ai, index); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FUNCTION); + } + + public static Box getFieldL(AI ai, Object value, String field) throws LeekRunException { + // value = getValue(value); + if (value instanceof ObjectLeekValue) { + return ((ObjectLeekValue) value).getFieldL(field); + } + if (value instanceof ClassLeekValue) { + return ((ClassLeekValue) value).getFieldL(field); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public static Object callMethod(AI ai, Object value, String method, Object... arguments) throws LeekRunException { + // Aucune méthode + ai.addSystemLog(AILog.ERROR, Error.UNKNOWN_METHOD, new String[] { getString(ai, value), method }); + return null; + } + + public static Object callSuperMethod(AI ai, Object value, String method, Object... arguments) throws LeekRunException { + // Aucune méthode + ai.addSystemLog(AILog.ERROR, Error.UNKNOWN_METHOD, new String[] { getString(ai, value), method }); + return null; + } + + public static int getType(Object v) { + if (v == null) return LeekValue.NULL; + if (v instanceof Boolean) return LeekValue.BOOLEAN; + if (v instanceof Number) return LeekValue.NUMBER; + if (v instanceof String) return LeekValue.STRING; + if (v instanceof ArrayLeekValue) return LeekValue.ARRAY; + if (v instanceof ObjectLeekValue) return LeekValue.OBJECT; + if (v instanceof ClassLeekValue) return LeekValue.CLASS; + if (v instanceof FunctionLeekValue) return LeekValue.FUNCTION; + if (v instanceof Box) return getType(((Box) v).getValue()); + return 0; + } + + public static int getV1Type(Object v) { + if (v == null) return LeekValue.NULL_V1; + if (v instanceof Boolean) return LeekValue.BOOLEAN_V1; + if (v instanceof Number) return LeekValue.NUMBER_V1; + if (v instanceof String) return LeekValue.STRING_V1; + if (v instanceof ArrayLeekValue) return LeekValue.ARRAY_V1; + if (v instanceof ObjectLeekValue) return LeekValue.OBJECT_V1; + if (v instanceof ClassLeekValue) return LeekValue.CLASS_V1; + if (v instanceof FunctionLeekValue) return LeekValue.FUNCTION_V1; + if (v instanceof Box) return getV1Type(((Box) v).getValue()); + return 0; + } + + public static String toJSON(AI ai, Object value) { + return null; + } + + public static Object executeArrayAccess(AI ai, Object array, Object key, ClassLeekValue fromClass, Object... arguments) throws LeekRunException { + if (array instanceof ObjectLeekValue) { + ai.ops(1); + return ((ObjectLeekValue) array).callMethod(ai.string(key) + "_" + arguments.length, fromClass, arguments); + } else { + return ai.execute(ai.get(array, key, fromClass), arguments); + } } } diff --git a/src/main/java/leekscript/runner/PhpArray.java b/src/main/java/leekscript/runner/PhpArray.java index ef07bc6e..5232f467 100644 --- a/src/main/java/leekscript/runner/PhpArray.java +++ b/src/main/java/leekscript/runner/PhpArray.java @@ -5,15 +5,17 @@ import java.util.Comparator; import java.util.Iterator; import java.util.List; +import java.util.Random; +import java.util.Set; -import leekscript.runner.values.AbstractLeekValue; import leekscript.runner.values.ArrayLeekValue; -import leekscript.runner.values.PhpArrayVariableLeekValue; -import leekscript.runner.values.StringLeekValue; +import leekscript.runner.values.LeekValue; +import leekscript.runner.values.ObjectLeekValue; +import leekscript.runner.values.Box; -public class PhpArray implements Iterable { +public class PhpArray implements Iterable { - private final static int START_CAPACITY = 16; + private final static int START_CAPACITY = 8; private final static int MAX_CAPACITY = 32000; public final static int ASC = 1; @@ -24,19 +26,77 @@ public class PhpArray implements Iterable { public final static int ASC_K = 6; public final static int DESC_K = 7; - private static int RAM_LIMIT = 1000000; + private class ElementComparatorV1 implements Comparator { + + private final int mOrder; + + public final static int SORT_ASC = 1; + public final static int SORT_DESC = 2; + + public ElementComparatorV1(int order) { + mOrder = order; + } + + @Override + public int compare(Element v1, Element v2) { + try { + if (mOrder == SORT_ASC) + return compareAsc(v1.value.getValue(), v2.value.getValue()); + else if (mOrder == SORT_DESC) + return compareAsc(v2.value.getValue(), v1.value.getValue()); + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + + public int compareAsc(Object v1, Object v2) throws LeekRunException { + int type1 = LeekValueManager.getV1Type(v1); + int type2 = LeekValueManager.getV1Type(v2); + if (type1 < type2) + return -1; + else if (type1 > type2) + return 1; + if (type1 == LeekValue.BOOLEAN_V1) { + if ((Boolean) v1 == (Boolean) v2) + return 0; + else if ((Boolean) v1) + return 1; + else + return -1; + } else if (type1 == LeekValue.NUMBER_V1) { + var d = ((Number) v2).doubleValue(); + if (((Number) v1).doubleValue() == d) + return 0; + else if (((Number) v1).doubleValue() < d) + return -1; + else + return 1; + } else if (type1 == LeekValue.STRING_V1) { + return ((String) v1).compareTo((String) v2); + } else if (type1 == LeekValue.ARRAY_V1) { + var a = (ArrayLeekValue) v2; + if (((ArrayLeekValue) v1).size() == a.size()) + return 0; + else if (((ArrayLeekValue) v1).size() < a.size()) + return -1; + else + return 1; + } else { + return 0; + } + } + } private class ElementComparator implements Comparator { private final int mOrder; - private final AI ai; public final static int SORT_ASC = 1; public final static int SORT_DESC = 2; - public ElementComparator(AI ai, int order) { + public ElementComparator(int order) { mOrder = order; - this.ai = ai; } @Override @@ -46,42 +106,47 @@ public int compare(Element v1, Element v2) { return compareAsc(v1.value.getValue(), v2.value.getValue()); else if (mOrder == SORT_DESC) return compareAsc(v2.value.getValue(), v1.value.getValue()); - } catch (Exception e) {} + } catch (Exception e) { + e.printStackTrace(); + } return 0; } - public int compareAsc(AbstractLeekValue v1, AbstractLeekValue v2) throws LeekRunException { - if (v1.getType() < v2.getType()) + public int compareAsc(Object v1, Object v2) throws LeekRunException { + int type1 = LeekValueManager.getType(v1); + int type2 = LeekValueManager.getType(v2); + if (type1 < type2) return -1; - else if (v1.getType() > v2.getType()) + else if (type1 > type2) return 1; - if (v1.getType() == AbstractLeekValue.BOOLEAN) { - if (v1.getBoolean() == v2.getBoolean()) + if (type1 == LeekValue.BOOLEAN) { + if ((Boolean) v1 == (Boolean) v2) return 0; - else if (v1.getBoolean()) + else if ((Boolean) v1) return 1; else return -1; - } else if (v1.getType() == AbstractLeekValue.NUMBER) { - if (v1.getDouble(ai) == v2.getDouble(ai)) + } else if (type1 == LeekValue.NUMBER) { + var d = ((Number) v2).doubleValue(); + if (((Number) v1).doubleValue() == d) return 0; - else if (v1.getDouble(ai) < v2.getDouble(ai)) + else if (((Number) v1).doubleValue() < d) return -1; else return 1; - } else if (v1.getType() == AbstractLeekValue.STRING) { - return v1.getString(ai).compareTo(v2.getString(ai)); - } else if (v1.getType() == AbstractLeekValue.ARRAY) { - if (v1.getArray().size() == v2.getArray().size()) + } else if (type1 == LeekValue.STRING) { + return ((String) v1).compareTo((String) v2); + } else if (type1 == LeekValue.ARRAY) { + var a = (ArrayLeekValue) v2; + if (((ArrayLeekValue) v1).size() == a.size()) return 0; - else if (v1.getArray().size() < v2.getArray().size()) + else if (((ArrayLeekValue) v1).size() < a.size()) return -1; else return 1; - } else if (v1.getType() == AbstractLeekValue.NULL) + } else { return 0; - else - return -1; + } } } @@ -116,25 +181,27 @@ else if (v1 instanceof Integer) } } - private class PhpIterator implements Iterator { + private class PhpIterator implements Iterator { private Element e = mHead; @Override public boolean hasNext() { return e != null; } + @Override - public AbstractLeekValue next() { - AbstractLeekValue v = e.value; + public Box next() { + var v = e.value; if (e != null) e = e.next; return v; } + @Override public void remove() {} } - private class ReversedPhpIterator implements Iterator { + private class ReversedPhpIterator implements Iterator { private Element e = mEnd; @Override @@ -142,8 +209,8 @@ public boolean hasNext() { return e != null; } @Override - public AbstractLeekValue next() { - AbstractLeekValue v = e.value; + public Object next() { + var v = e.value.getValue(); if (e != null) e = e.prev; return v; @@ -153,10 +220,11 @@ public void remove() {} } public class Element { + private Object key; private int hash; private boolean numeric = false; - private PhpArrayVariableLeekValue value = null; + private Box value = null; private Element next = null; private Element prev = null; @@ -167,77 +235,87 @@ public Element next() { return next; } - public AbstractLeekValue key() { - if (key instanceof Integer) - return LeekValueManager.getLeekIntValue(((Integer) key).intValue()); - else - return new StringLeekValue(key.toString()); + public Object key() { + return key; } public Object keyObject() { return key; } - public AbstractLeekValue value() { + public Object value() { return value.getValue(); } - public void setValue(AI ai, AbstractLeekValue v) throws Exception { - value.set(ai, v.getValue()); + public Object valueBox() { + return value; + } + + public void setValue(AI ai, Object v) throws LeekRunException { + value.set(v); + } + + public String toString() { + return value.getValue().toString(); } } private Element mHead = null; private Element mEnd = null; - private int mIndex = 0; - private int mSize = 0; private int capacity = 0; - - // Calcul optimisé de la ram utilisée (Optimisé niveau UC, pas niveau RAM) - private int mTotalSize = 0; - private PhpArrayVariableLeekValue mParent = null; - private Element[] mTable = null; public PhpArray() {} - + public PhpArray(AI ai, int capacity) throws LeekRunException { - initTable(ai, capacity); + if (capacity > 0) { + initTable(ai, capacity); + } } - - public PhpArray(AI ai, PhpArray phpArray) throws Exception { + + public PhpArray(AI ai, PhpArray phpArray, int level) throws LeekRunException { if (phpArray.size() > 0) { initTable(ai, phpArray.size()); Element e = phpArray.mHead; while (e != null) { - set(ai, e.key, LeekOperations.clone(ai, e.value.getValue())); + if (ai.getVersion() >= 2) { + if (level == 1) { + set(ai, e.key, e.value.getValue()); + } else { + set(ai, e.key, LeekOperations.clone(ai, e.value.getValue(), level - 1)); + } + } else { + set(ai, e.key, LeekOperations.clone(ai, e.value.getValue())); + } e = e.next; } } } - + private void initTable(AI ai, int capacity) throws LeekRunException { - ai.addOperations(capacity / 5); - this.capacity = capacity; - mTable = new Element[capacity]; + int realCapacity = Math.max(START_CAPACITY, capacity); + // System.out.println("ops initTable"); + ai.addOperationsNoCheck(realCapacity / 5); + this.capacity = realCapacity; + mTable = new Element[realCapacity]; } - - private void growCapacity(AI ai) throws Exception { + + private void growCapacity(AI ai) throws LeekRunException { if (capacity == MAX_CAPACITY) return; - - capacity = Math.min(capacity * 2, MAX_CAPACITY); - + // Copy in a new array - PhpArray newArray = new PhpArray(ai, capacity); + int new_capacity = Math.min(Math.max(START_CAPACITY, capacity * 2), MAX_CAPACITY); + PhpArray newArray = new PhpArray(ai, new_capacity); Element e = mHead; while (e != null) { newArray.set(ai, e.key, e.value.getValue()); e = e.next; } // Use the table of this new array + capacity = new_capacity; mTable = newArray.mTable; mHead = newArray.mHead; mEnd = newArray.mEnd; @@ -266,9 +344,14 @@ public int size() { * @return Valeur à la clé donnée * @throws LeekRunException */ - public AbstractLeekValue get(AI ai, Object key) throws LeekRunException { + public Object get(AI ai, Object key) throws LeekRunException { + Element e = getElement(ai, key); + return e == null ? null : e.value.getValue(); + } + + public Box getBox(AI ai, Object key) throws LeekRunException { Element e = getElement(ai, key); - return e == null ? LeekValueManager.NULL : e.value; + return e == null ? new Box(ai) : e.value; } /** @@ -289,12 +372,12 @@ public boolean containsKey(AI ai, Object key) throws LeekRunException { * @param value * Valeur à rechercher * @return True si la valeur existe dans le tableau - * @throws LeekRunException + * @throws LeekRunException */ - public boolean contains(AI ai, AbstractLeekValue value) throws LeekRunException { + public boolean contains(AI ai, Object value) throws LeekRunException { Element e = mHead; while (e != null) { - if (e.value.equals(ai, value)) + if (ai.eq(e.value.getValue(), value)) return true; e = e.next; } @@ -308,27 +391,24 @@ public boolean contains(AI ai, AbstractLeekValue value) throws LeekRunException * Valeur à rechercher * @param pos * @return Clé associée à la valeur ou null si la valeur n'existe pas - * @throws LeekRunException + * @throws LeekRunException */ - public AbstractLeekValue search(AI ai, AbstractLeekValue value, int pos) throws LeekRunException { + public Object search(AI ai, Object value, int pos) throws LeekRunException { Element e = mHead; int p = 0; while (e != null) { - if (p >= pos && e.value.getType() == value.getType() && e.value.equals(ai, value)) { - if (e.key instanceof Integer) - return LeekValueManager.getLeekIntValue(((Integer) e.key).intValue()); - else - return new StringLeekValue(e.key.toString()); + if (p >= pos && LeekValueManager.getType(e.value) == LeekValueManager.getType(value) && ai.eq(e.value.getValue(), value)) { + return e.key; } e = e.next; p++; } - return LeekValueManager.NULL; + return null; } - public AbstractLeekValue removeIndex(AI ai, int index) throws LeekRunException { + public Object removeIndex(AI ai, int index) throws LeekRunException { if (index >= mSize) - return LeekValueManager.NULL; + return null; Element e = mHead; int p = 0; while (e != null) { @@ -340,7 +420,7 @@ public AbstractLeekValue removeIndex(AI ai, int index) throws LeekRunException { e = e.next; p++; } - return LeekValueManager.NULL; + return null; } /** @@ -380,6 +460,7 @@ public void sort(AI ai, int comparator) throws LeekRunException { if (mSize == 0) { return; } + // System.out.println("sort " + comparator); // création de la liste List liste = new ArrayList(); Element elem = mHead; @@ -389,15 +470,22 @@ public void sort(AI ai, int comparator) throws LeekRunException { } // Trie de la liste if (comparator == RANDOM) - Collections.shuffle(liste); + Collections.shuffle(liste, new Random(ai.getRandom().getInt(0, Integer.MAX_VALUE - 1))); else if (comparator == ASC_K || comparator == DESC_K) { Collections.sort(liste, new KeyComparator( (comparator == ASC_K) ? ElementComparator.SORT_ASC : ElementComparator.SORT_DESC)); - } else - Collections.sort(liste, new ElementComparator(ai, + } else { + if (ai.getVersion() == 1) { + Collections.sort(liste, new ElementComparatorV1( (comparator == ASC || comparator == ASC_A) ? ElementComparator.SORT_ASC : ElementComparator.SORT_DESC)); + } else { + Collections.sort(liste, new ElementComparator( + (comparator == ASC || comparator == ASC_A) ? ElementComparator.SORT_ASC + : ElementComparator.SORT_DESC)); + } + } // Mise en place de la liste mHead = liste.get(0); @@ -420,9 +508,9 @@ else if (comparator == ASC_K || comparator == DESC_K) { * Trie le tableau * * @param comparator - * @throws Exception + * @throws LeekRunException */ - public void sort(AI ai, Comparator comparator) throws Exception { + public void sort(AI ai, Comparator comparator) throws LeekRunException { if (mSize == 0) return; @@ -517,10 +605,10 @@ public void assocReverse() { mEnd = prev; } - public void removeObject(AI ai, AbstractLeekValue value) throws LeekRunException { + public void removeObject(AI ai, Object value) throws LeekRunException { Element e = mHead; while (e != null) { - if (e.value.getType() == value.getType() && e.value.equals(ai, value)) { + if (LeekValueManager.getType(e.value) == LeekValueManager.getType(value) && ai.eq(e.value.getValue(), value)) { // On a notre élément à supprimer // On l'enleve de la HashMap removeFromHashmap(ai, e); @@ -546,16 +634,16 @@ public void removeObject(AI ai, AbstractLeekValue value) throws LeekRunException * * @param value * Element à ajouter - * @throws Exception + * @throws LeekRunException */ - public void push(AI ai, AbstractLeekValue value) throws Exception { + public void push(AI ai, Object value) throws LeekRunException { + if (mSize >= capacity) { + growCapacity(ai); + } + mSize++; Integer key = Integer.valueOf(mIndex); Element e = createElement(ai, key, value); pushElement(e); - mSize++; - if (mSize > capacity) { - growCapacity(ai); - } } /** @@ -563,17 +651,17 @@ public void push(AI ai, AbstractLeekValue value) throws Exception { * * @param value * Element à ajouter - * @throws Exception + * @throws LeekRunException */ - public void unshift(AI ai, AbstractLeekValue value) throws Exception { + public void unshift(AI ai, Object value) throws LeekRunException { + if (mSize >= capacity) { + growCapacity(ai); + } + mSize++; Integer key = 0; Element e = createElement(ai, key, value); unshiftElement(e); reindex(ai); - mSize++; - if (mSize > capacity) { - growCapacity(ai); - } } /** @@ -583,51 +671,50 @@ public void unshift(AI ai, AbstractLeekValue value) throws Exception { * Clé (Integer, Double ou String) * @param value * Valeur - * @throws Exception + * @throws LeekRunException */ - public void set(AI ai, Object key, AbstractLeekValue value) throws Exception { + public void set(AI ai, Object key, Object value) throws LeekRunException { Element e = getElement(ai, key); // Si l'élément n'existe pas on le crée if (e == null) { - e = createElement(ai, key, value); - pushElement(e); - mSize++; - if (mSize > capacity) { + if (mSize >= capacity) { growCapacity(ai); } + mSize++; + e = createElement(ai, key, value); + pushElement(e); } else { - e.value.set(ai, value); + e.value.set(value); } } - public AbstractLeekValue getOrCreate(AI ai, Object key) throws Exception { + public Box getOrCreate(AI ai, Object key) throws LeekRunException { Element e = getElement(ai, key); if (e == null) { - e = createElement(ai, key, LeekValueManager.NULL); - pushElement(e); - mSize++; - if (mSize > capacity) { + if (mSize >= capacity) { growCapacity(ai); - e = getElement(ai, key); } + mSize++; + e = createElement(ai, key, null); + pushElement(e); } return e.value; } - public void set(int key, AbstractLeekValue value) throws Exception { + public void set(int key, Object value) throws LeekRunException { set(Integer.valueOf(key), value); } - public AbstractLeekValue end() { - return mEnd == null ? LeekValueManager.NULL : mEnd.value; + public Box end() { + return mEnd == null ? null : mEnd.value; } - public AbstractLeekValue start() { - return mHead == null ? LeekValueManager.NULL : mHead.value; + public Box start() { + return mHead == null ? null : mHead.value; } - public void insert(AI ai, int position, AbstractLeekValue value) throws Exception { + public void insert(AI ai, int position, Object value) throws LeekRunException { if (position < 0) { return; } else if (position >= mSize) { @@ -635,6 +722,10 @@ public void insert(AI ai, int position, AbstractLeekValue value) throws Exceptio } else if (position == 0) { unshift(ai, value); } else { + if (mSize >= capacity) { + growCapacity(ai); + } + mSize++; // On crée notre nouvel élément Element e = createElement(ai, Integer.valueOf(mIndex), value); // On va rechercher l'élément avant lequel insérer @@ -650,21 +741,17 @@ public void insert(AI ai, int position, AbstractLeekValue value) throws Exceptio // On réindexe reindex(ai); - - mSize++; - if (mSize > capacity) { - growCapacity(ai); - } } } // Fonctions "briques de base" public void reindex(AI ai) throws LeekRunException { - // Réindexer le tableau (Change l'index de toutes les valeurs - // numériques) + // Réindexer le tableau (Change l'index de toutes les valeurs numériques) int new_index = 0; - + + ai.addOperationsNoCheck(mSize); + Element e = mHead; while (e != null) { if (e.numeric) { @@ -686,11 +773,11 @@ public void reindex(AI ai) throws LeekRunException { mIndex = new_index; } - private void unshiftElement(Element e) {// Ajouter un élément au début - if (mHead == null) {// Tableau vide + private void unshiftElement(Element e) { // Ajouter un élément au début + if (mHead == null) { // Tableau vide mHead = e; mEnd = e; - } else {// Ajouter au début + } else { // Ajouter au début mHead.prev = e; e.next = mHead; mHead = e; @@ -698,26 +785,23 @@ private void unshiftElement(Element e) {// Ajouter un élément au début } private void pushElement(Element e) {// Ajouter un élément à la fin - if (mEnd == null) {// Tableau vide + if (mEnd == null) { // Tableau vide mHead = e; mEnd = e; - } else {// Sinon on ajoute à la fin + } else { // Sinon on ajoute à la fin mEnd.next = e; e.prev = mEnd; mEnd = e; } } - private Element createElement(AI ai, Object key, AbstractLeekValue value) throws Exception { + private Element createElement(AI ai, Object key, Object value) throws LeekRunException { // On crée l'élément Element e = new Element(); e.hash = key.hashCode(); e.key = key; - // On ajoute la taille de la clé - int keySize = 1; - - e.value = new PhpArrayVariableLeekValue(this, ai, value, keySize); + e.value = new Box(ai, value); if (key instanceof Integer) { // On met à jour l'index suivant int index = ((Integer) key).intValue(); @@ -729,8 +813,9 @@ private Element createElement(AI ai, Object key, AbstractLeekValue value) throws // On l'ajoute dans la hashmap addToHashMap(ai, e); + // System.out.println("ops createElement"); int operations = ArrayLeekValue.ARRAY_CELL_CREATE_OPERATIONS + (int) Math.sqrt(mSize) / 3; - ai.addOperations(operations); + ai.addOperationsNoCheck(operations); return e; } @@ -778,20 +863,19 @@ else if (f == e) { private void destroyElement(Element e) throws LeekRunException { mSize--; - if (e.key instanceof Integer) - e.value.removeFromTable(1); - else - e.value.removeFromTable(((String) e.key).length()); } private Element getElement(AI ai, Object key) throws LeekRunException { + + // System.out.println("ops getElement"); + int operations = ArrayLeekValue.ARRAY_CELL_ACCESS_OPERATIONS; + ai.addOperationsNoCheck(operations); + if (mTable == null) { return null; // empty array } - int operations = ArrayLeekValue.ARRAY_CELL_ACCESS_OPERATIONS; - ai.addOperations(operations); - int hash = getHash(key); + int hash = key == null ? 0 : key.hashCode(); int index = getIndex(hash); Element f = mTable[index]; @@ -805,17 +889,13 @@ private Element getElement(AI ai, Object key) throws LeekRunException { return null; } - private int getHash(Object key) { - return key.hashCode(); - } - private int getIndex(int hash) { return hash & (capacity - 1); } - public String toString(AI ai) throws LeekRunException { + public String toString(AI ai, Set visited) throws LeekRunException { - ai.addOperations(1 + mSize * 2); + ai.ops(1 + mSize * 2); StringBuilder sb = new StringBuilder(); // On va regarder si le tableau est dans l'ordre @@ -837,9 +917,22 @@ public String toString(AI ai) throws LeekRunException { while (e != null) { if (e != mHead) sb.append(", "); - if (!isInOrder) - sb.append(e.key).append(" : "); - sb.append(e.value.getString(ai)); + if (!isInOrder) { + if (e.key instanceof ObjectLeekValue) { + sb.append(ai.getString((ObjectLeekValue) e.key, visited)); + } else { + sb.append(e.key); + } + sb.append(" : "); + } + if (visited.contains(e.value.getValue())) { + sb.append("<...>"); + } else { + if (!ai.isPrimitive(e.value.getValue())) { + visited.add(e.value.getValue()); + } + sb.append(ai.getString(e.value.getValue(), visited)); + } e = e.next; } sb.append("]"); @@ -861,58 +954,37 @@ public boolean isAssociative() { } @Override - public Iterator iterator() { + public Iterator iterator() { return new PhpIterator(); } - public Iterator reversedIterator() { + public Iterator reversedIterator() { return new ReversedPhpIterator(); } public boolean equals(AI ai, PhpArray array) throws LeekRunException { - - ai.addOperations(1); - + + ai.ops(1); + // On commence par vérifier la taille if (mSize != array.mSize) return false; if (mSize == 0) return true; - ai.addOperations(mSize); - + ai.ops(mSize); + Element e1 = mHead; Element e2 = array.mHead; // On va comparer chaque élément 1 à 1 while (e1 != null) { if (!e1.key.equals(e2.key)) return false; - if (!e1.value.getValue().equals(ai, e2.value.getValue())) + if (!ai.eq(e1.value.getValue(), e2.value.getValue())) return false; e1 = e1.next; e2 = e2.next; } return true; } - - public void setParent(PhpArrayVariableLeekValue parent) { - mParent = parent; - } - - public void updateArraySize(int delta) throws LeekRunException { - if (delta == 0) { - return; - } - mTotalSize += delta; - if (mTotalSize >= RAM_LIMIT) { - throw new LeekRunException(LeekRunException.OUT_OF_MEMORY); - } - if (mParent != null) { - mParent.updateSize(delta); - } - } - - public int getSize() { - return mTotalSize; - } } diff --git a/src/main/java/leekscript/runner/Wrapper.java b/src/main/java/leekscript/runner/Wrapper.java new file mode 100644 index 00000000..8e5b6c43 --- /dev/null +++ b/src/main/java/leekscript/runner/Wrapper.java @@ -0,0 +1,109 @@ + +package leekscript.runner; + +import leekscript.runner.values.Box; + +public class Wrapper { + + private Box variable; + + public Wrapper(Box variable) { + this.variable = variable; + } + + public Wrapper(Box variable, int ops) throws LeekRunException { + this.variable = variable; + this.variable.getAI().ops(ops); + } + + public Object setBox(Box variable) { + this.variable = variable; + return this.variable.getValue(); + } + + public Object set(Object value) throws LeekRunException { + this.variable.set(value); + return value; + } + + public Object setBoxOrValue(Object value) throws LeekRunException { + if (value instanceof Box) { + this.variable = (Box) value; + return this.variable.getValue(); + } else { + return this.variable.set(value); + } + } + + public Box getVariable() { + return variable; + } + + public Object getValue() { + return variable.getValue(); + } + + public Object increment() throws LeekRunException { + return variable.increment(); + } + + public Object pre_increment() throws LeekRunException { + return variable.pre_increment(); + } + + public Object decrement() throws LeekRunException { + return variable.decrement(); + } + + public Object pre_decrement() throws LeekRunException { + return variable.pre_decrement(); + } + + public Object add_eq(Object x) throws LeekRunException { + return variable.add_eq(x); + } + + public Object sub_eq(Object x) throws LeekRunException { + return variable.add_eq(x); + } + + public Object mul_eq(Object x) throws LeekRunException { + return variable.mul_eq(x); + } + + public Object div_eq(Object x) throws LeekRunException { + return variable.div_eq(x); + } + + public Object mod_eq(Object x) throws LeekRunException { + return variable.mod_eq(x); + } + + public Object pow_eq(Object x) throws LeekRunException { + return variable.pow_eq(x); + } + + public int band_eq(Object x) throws LeekRunException { + return variable.band_eq(x); + } + + public int bor_eq(Object x) throws LeekRunException { + return variable.bor_eq(x); + } + + public int bxor_eq(Object x) throws LeekRunException { + return variable.bxor_eq(x); + } + + public int shl_eq(Object x) throws LeekRunException { + return variable.shl_eq(x); + } + + public int shr_eq(Object x) throws LeekRunException { + return variable.shr_eq(x); + } + + public int ushr_eq(Object x) throws LeekRunException { + return variable.ushr_eq(x); + } +} \ No newline at end of file diff --git a/src/main/java/leekscript/runner/values/AbstractLeekValue.java b/src/main/java/leekscript/runner/values/AbstractLeekValue.java deleted file mode 100644 index b606ee63..00000000 --- a/src/main/java/leekscript/runner/values/AbstractLeekValue.java +++ /dev/null @@ -1,219 +0,0 @@ -package leekscript.runner.values; - -import leekscript.runner.AI; -import leekscript.runner.LeekRunException; -import leekscript.runner.LeekValueManager; - -public abstract class AbstractLeekValue { - - public final static int NUMBER = 1; - public final static int BOOLEAN = 2; - public final static int ARRAY = 3; - public final static int NULL = 4; - public final static int STRING = 5; - public final static int FUNCTION = 6; - - public final static int ADD_COST = 1; - public final static int MUL_COST = 5; - public final static int DIV_COST = 5; - public final static int MOD_COST = 5; - public final static int POW_COST = 140; - - public int getSize() throws LeekRunException { - return 1; - } - - public int getInt(AI ai) throws LeekRunException { - return 0; - } - - public void setInt(int nb) {} - - public double getDouble(AI ai) throws LeekRunException { - return 0; - } - - public String getString(AI ai) throws LeekRunException { - return ""; - } - - public boolean getBoolean() { - return false; - } - - public boolean isNumeric() { - return false; - } - - public boolean isArray() { - return false; - } - - public boolean isNull() { - return false; - } - - public ArrayLeekValue getArray() { - return null; - } - - public AbstractLeekValue get(AI ai, int value) throws LeekRunException, Exception { - return LeekValueManager.NULL; - } - - public AbstractLeekValue get(AI ai, AbstractLeekValue value) throws Exception { - return LeekValueManager.NULL; - } - - public AbstractLeekValue getOrCreate(AI ai, AbstractLeekValue value) throws Exception { - return LeekValueManager.NULL; - } - - public AbstractLeekValue getValue() { - return this; - } - - public AbstractLeekValue not(AI ai) { - return LeekValueManager.getLeekBooleanValue(!getBoolean()); - } - - public AbstractLeekValue opposite(AI ai) throws Exception { - return LeekValueManager.getLeekIntValue(-getInt(ai)); - } - - public AbstractLeekValue set(AI ai, AbstractLeekValue value) throws LeekRunException, Exception { - return this; - } - - public int getArgumentsCount(AI ai) throws Exception { - return -1; - } - - // Fonctions de comparaison - public abstract boolean equals(AI ai, AbstractLeekValue comp) throws LeekRunException; - - public boolean notequals(AI ai, AbstractLeekValue comp) throws LeekRunException { - return !equals(ai, comp); - } - - public boolean less(AI ai, AbstractLeekValue comp) throws LeekRunException { - ai.addOperations(1); - return getInt(ai) < comp.getInt(ai); - } - - public boolean moreequals(AI ai, AbstractLeekValue comp) throws LeekRunException { - return !less(ai, comp); - } - - public boolean more(AI ai, AbstractLeekValue comp) throws LeekRunException { - ai.addOperations(1); - return getInt(ai) > comp.getInt(ai); - } - - public boolean lessequals(AI ai, AbstractLeekValue comp) throws LeekRunException { - return !more(ai, comp); - } - - // Fonctions pour L-Values - public AbstractLeekValue increment(AI ai) throws Exception { - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); - } - - public AbstractLeekValue decrement(AI ai) throws Exception { - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); - } - - public AbstractLeekValue pre_increment(AI ai) throws Exception { - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); - } - - public AbstractLeekValue pre_decrement(AI ai) throws Exception { - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); - } - - public AbstractLeekValue add(AI ai, AbstractLeekValue value) throws Exception { - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); - } - - public AbstractLeekValue minus(AI ai, AbstractLeekValue value) throws Exception { - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); - } - - public AbstractLeekValue multiply(AI ai, AbstractLeekValue value) throws Exception { - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); - } - - public AbstractLeekValue divide(AI ai, AbstractLeekValue value) throws Exception { - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); - } - - public AbstractLeekValue modulus(AI ai, AbstractLeekValue value) throws Exception { - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); - } - - public AbstractLeekValue power(AI ai, AbstractLeekValue value) throws Exception { - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); - } - - public AbstractLeekValue band(AI ai, AbstractLeekValue value) throws Exception { - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); - } - - public AbstractLeekValue bor(AI ai, AbstractLeekValue value) throws Exception { - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); - } - - public AbstractLeekValue bxor(AI ai, AbstractLeekValue value) throws Exception { - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); - } - - public AbstractLeekValue bleft(AI ai, AbstractLeekValue value) throws Exception { - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); - } - - public AbstractLeekValue bright(AI ai, AbstractLeekValue value) throws Exception { - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); - } - - public AbstractLeekValue brotate(AI ai, AbstractLeekValue value) throws Exception { - throw new LeekRunException(LeekRunException.INVALID_OPERATOR); - } - - public abstract int getType(); - - public boolean isReference() { - return false; - } - - public abstract Object toJSON(AI ai) throws Exception; - - public AbstractLeekValue executeFunction(AI ai, AbstractLeekValue[] value) throws Exception { - - // On ne peux pas exécuter ce type de variable - ai.addOperations(AI.ERROR_LOG_COST); - return LeekValueManager.NULL; - } - - public static String getParamString(AbstractLeekValue[] parameters) { - String ret = ""; - for (int j = 0; j < parameters.length; j++) { - if (j != 0) - ret += ", "; - if (parameters[j].getValue().getType() == NUMBER) - ret += "number"; - else if (parameters[j].getValue().getType() == BOOLEAN) - ret += "boolean"; - else if (parameters[j].getValue().getType() == STRING) - ret += "string"; - else if (parameters[j].getValue().getType() == ARRAY) - ret += "array"; - else if (parameters[j].getValue().getType() == FUNCTION) - ret += "function"; - else if (parameters[j].getValue().getType() == NULL) - ret += "null"; - else - ret += "?"; - } - return ret; - } -} diff --git a/src/main/java/leekscript/runner/values/ArrayLeekValue.java b/src/main/java/leekscript/runner/values/ArrayLeekValue.java index ad8b45e1..d73e5599 100644 --- a/src/main/java/leekscript/runner/values/ArrayLeekValue.java +++ b/src/main/java/leekscript/runner/values/ArrayLeekValue.java @@ -1,17 +1,21 @@ package leekscript.runner.values; import java.util.Comparator; +import java.util.HashSet; import java.util.Iterator; +import java.util.Set; import leekscript.runner.AI; import leekscript.runner.LeekOperations; import leekscript.runner.LeekRunException; +import leekscript.runner.LeekValueManager; import leekscript.runner.PhpArray; +import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; -public class ArrayLeekValue extends AbstractLeekValue implements Iterable { +public class ArrayLeekValue implements Iterable { private final PhpArray mValues; @@ -34,27 +38,39 @@ public void next() { mElement = mElement.next(); } - public AbstractLeekValue getKey(AI ai) throws Exception { - return LeekOperations.clone(ai, mElement.key()); + public Object getKey(AI ai) throws LeekRunException { + if (ai.getVersion() >= 2) { + return mElement.key(); + } else { + return LeekOperations.clone(ai, mElement.key()); + } } public Object key() { return mElement.keyObject(); } - public AbstractLeekValue getValue(AI ai) throws Exception { - return LeekOperations.clone(ai, mElement.value()); + public Object getValue(AI ai) throws LeekRunException { + if (ai.getVersion() >= 2) { + return mElement.value(); + } else { + return LeekOperations.clone(ai, mElement.value()); + } + } + + public Object value() { + return mElement.value(); } - public AbstractLeekValue getKeyReference() throws Exception { + public Object getKeyRef() throws LeekRunException { return mElement.key(); } - public AbstractLeekValue getValueReference() throws Exception { - return mElement.value(); + public Object getValueBox() throws LeekRunException { + return mElement.valueBox(); } - public void setValue(AI ai, VariableLeekValue value) throws Exception { + public void setValue(AI ai, Box value) throws LeekRunException { mElement.setValue(ai, value); } } @@ -63,16 +79,16 @@ public ArrayLeekValue() { mValues = new PhpArray(); } - public ArrayLeekValue(AI ai, AbstractLeekValue values[]) throws Exception { + public ArrayLeekValue(AI ai, Object values[]) throws LeekRunException { this(ai, values, false); } - public ArrayLeekValue(AI ai, AbstractLeekValue values[], boolean isKeyValue) throws Exception { + public ArrayLeekValue(AI ai, Object values[], boolean isKeyValue) throws LeekRunException { mValues = new PhpArray(ai, values.length); if (isKeyValue) { int i = 0; while (i < values.length) { - getOrCreate(ai, values[i].getValue()).set(ai, values[i + 1].getValue()); + getOrCreate(ai, values[i]).set(values[i + 1]); i += 2; } } else { @@ -82,63 +98,170 @@ public ArrayLeekValue(AI ai, AbstractLeekValue values[], boolean isKeyValue) thr } } - /* - * public ArrayLeekValue() { mValues = new PhpArray(); } - */ - - public ArrayLeekValue(AI ai, ArrayLeekValue array) throws Exception { - mValues = new PhpArray(ai, array.mValues); + public ArrayLeekValue(AI ai, ArrayLeekValue array, int level) throws LeekRunException { + mValues = new PhpArray(ai, array.mValues, level); } public int size() { return mValues.size(); } - @Override - public AbstractLeekValue get(AI ai, AbstractLeekValue value) throws Exception { - Object key; - value = value.getValue(); - if (value instanceof StringLeekValue) - key = value.getString(ai); - else - key = Integer.valueOf(value.getInt(ai)); + public Object get(AI ai, Object keyValue) throws LeekRunException { + var key = transformKey(ai, keyValue); return mValues.get(ai, key); } - @Override - public AbstractLeekValue getOrCreate(AI ai, AbstractLeekValue value) throws Exception { - Object key; - value = value.getValue(); - if (value instanceof StringLeekValue) { - key = value.getString(ai); + public Box getBox(AI ai, Object keyValue) throws LeekRunException { + var key = transformKey(ai, keyValue); + return mValues.getBox(ai, key); + } + + public Box getOrCreate(AI ai, Object keyValue) throws LeekRunException { + var key = transformKey(ai, keyValue); + return mValues.getOrCreate(ai, key); + } + + public Object put(AI ai, Object keyValue, Object value) throws LeekRunException { + // ai.ops(1); + var key = transformKey(ai, keyValue); + if (ai.getVersion() == 1) { + value = LeekOperations.clone(ai, value); + } + mValues.set(ai, key, value); + return value; + } + + public Object put_inc(AI ai, Object keyValue) throws LeekRunException { + // ai.ops(1); + var key = transformKey(ai, keyValue); + return mValues.getOrCreate(ai, key).increment(); + } + + public Object put_pre_inc(AI ai, Object keyValue) throws LeekRunException { + // ai.ops(1); + var key = transformKey(ai, keyValue); + return mValues.getOrCreate(ai, key).pre_increment(); + } + + public Object put_dec(AI ai, Object keyValue) throws LeekRunException { + // ai.ops(1); + var key = transformKey(ai, keyValue); + return mValues.getOrCreate(ai, key).decrement(); + } + + public Object put_pre_dec(AI ai, Object keyValue) throws LeekRunException { + // ai.ops(1); + var key = transformKey(ai, keyValue); + return mValues.getOrCreate(ai, key).pre_decrement(); + } + + public Object put_add_eq(AI ai, Object keyValue, Object value) throws LeekRunException { + // ai.ops(1); + var key = transformKey(ai, keyValue); + mValues.getOrCreate(ai, key).add_eq(value); + return value; + } + + public Object put_sub_eq(AI ai, Object keyValue, Object value) throws LeekRunException { + // ai.ops(1); + var key = transformKey(ai, keyValue); + mValues.getOrCreate(ai, key).sub_eq(value); + return value; + } + + public Object put_mul_eq(AI ai, Object keyValue, Object value) throws LeekRunException { + // ai.ops(1); + var key = transformKey(ai, keyValue); + mValues.getOrCreate(ai, key).mul_eq(value); + return value; + } + + public Object put_pow_eq(AI ai, Object keyValue, Object value) throws LeekRunException { + // ai.ops(1); + var key = transformKey(ai, keyValue); + mValues.getOrCreate(ai, key).pow_eq(value); + return value; + } + + public Object put_div_eq(AI ai, Object keyValue, Object value) throws LeekRunException { + // ai.ops(1); + var key = transformKey(ai, keyValue); + mValues.getOrCreate(ai, key).div_eq(value); + return value; + } + + public Object put_mod_eq(AI ai, Object keyValue, Object value) throws LeekRunException { + // ai.ops(1); + var key = transformKey(ai, keyValue); + mValues.getOrCreate(ai, key).mod_eq(value); + return value; + } + + public Object put_bor_eq(AI ai, Object keyValue, Object value) throws LeekRunException { + // ai.ops(1); + var key = transformKey(ai, keyValue); + mValues.getOrCreate(ai, key).bor_eq(value); + return value; + } + + + public Object put_band_eq(AI ai, Object keyValue, Object value) throws LeekRunException { + // ai.ops(1); + var key = transformKey(ai, keyValue); + mValues.getOrCreate(ai, key).band_eq(value); + return value; + } + + + public Object put_bxor_eq(AI ai, Object keyValue, Object value) throws LeekRunException { + // ai.ops(1); + var key = transformKey(ai, keyValue); + return mValues.getOrCreate(ai, key).bxor_eq(value); + } + + public Object put_shl_eq(AI ai, Object keyValue, Object value) throws LeekRunException { + // ai.ops(1); + var key = transformKey(ai, keyValue); + return mValues.getOrCreate(ai, key).shl_eq(value); + } + + public Object put_shr_eq(AI ai, Object keyValue, Object value) throws LeekRunException { + // ai.ops(1); + var key = transformKey(ai, keyValue); + return mValues.getOrCreate(ai, key).shr_eq(value); + } + + public Object put_ushr_eq(AI ai, Object keyValue, Object value) throws LeekRunException { + // ai.ops(1); + var key = transformKey(ai, keyValue); + return mValues.getOrCreate(ai, key).ushr_eq(value); + } + + private Object transformKey(AI ai, Object key) throws LeekRunException { + if (key instanceof String || key instanceof ObjectLeekValue) { + return key; } else { - key = Integer.valueOf(value.getInt(ai)); + return ai.integer(key); } - return mValues.getOrCreate(ai, key); } - @Override - public AbstractLeekValue get(AI ai, int value) throws Exception { + public Box get(AI ai, int value) throws LeekRunException { return mValues.getOrCreate(ai, Integer.valueOf(value)); } - public AbstractLeekValue remove(AI ai, int index) throws LeekRunException { + public Object remove(AI ai, int index) throws LeekRunException { return mValues.removeIndex(ai, index); } - public void removeObject(AI ai, AbstractLeekValue value) throws LeekRunException { + public void removeObject(AI ai, Object value) throws LeekRunException { mValues.removeObject(ai, value); } - public void removeByKey(AI ai, AbstractLeekValue value) throws LeekRunException { - if (value.getType() == STRING) - mValues.remove(ai, value.getString(ai)); - else if (value.getType() == NUMBER) - mValues.remove(ai, value.getInt(ai)); + public void removeByKey(AI ai, Object value) throws LeekRunException { + mValues.remove(ai, value); } public void shuffle(AI ai) throws LeekRunException { - // Collections.shuffle(mValues); mValues.sort(ai, PhpArray.RANDOM); } @@ -153,116 +276,92 @@ public void assocReverse() { public String join(AI ai, String sep) throws LeekRunException { StringBuilder sb = new StringBuilder(); boolean first = true; - for (AbstractLeekValue val : mValues) { + for (Object val : mValues) { if (!first) sb.append(sep); else first = false; - sb.append(val.getValue().getString(ai)); + sb.append(LeekValueManager.getString(ai, val)); } return sb.toString(); } - public void insert(AI ai, AbstractLeekValue value, int pos) throws Exception { + public void insert(AI ai, Object value, int pos) throws LeekRunException { mValues.insert(ai, pos, value); } - public AbstractLeekValue search(AI ai, AbstractLeekValue search, int pos) throws LeekRunException { + public Object search(AI ai, Object search, int pos) throws LeekRunException { return mValues.search(ai, search, pos); } public void sort(AI ai, int type) throws LeekRunException { - // Collections.sort(mValues, new - // LeekValueComparator.SortComparator(type)); mValues.sort(ai, type); } @Override - public Iterator iterator() { + public Iterator iterator() { return mValues.iterator(); } - public AbstractLeekValue end() { + public Object end() { return mValues.end().getValue(); } - public AbstractLeekValue start() { + public Object start() { return mValues.start().getValue(); } - public boolean contains(AI ai, AbstractLeekValue value) throws LeekRunException { + public boolean contains(AI ai, Object value) throws LeekRunException { return mValues.contains(ai, value); } - public void push(AI ai, AbstractLeekValue m) throws Exception { + public void push(AI ai, Object m) throws LeekRunException { mValues.push(ai, m); } - @Override - public int getType() { - return ARRAY; - } - - @Override - public boolean isArray() { - return true; - } - - @Override - public ArrayLeekValue getArray() { - return this; - } - - public String getString(AI ai) throws LeekRunException { - return mValues.toString(ai); - } - - @Override - public boolean getBoolean() { - return mValues.size() != 0; + public String getString(AI ai, Set visited) throws LeekRunException { + visited.add(this); + return mValues.toString(ai, visited); } - @Override - public boolean equals(AI ai, AbstractLeekValue comp) throws LeekRunException { - if (comp.getType() == ARRAY) { - return mValues.equals(ai, comp.getArray().mValues); - } else if (mValues.size() == 1) {// Si y'a un seul élément dans le - // tableau - return mValues.getHeadElement().value().equals(ai, comp); - } else if (comp.getType() == BOOLEAN) { - return comp.getBoolean() == getBoolean(); - } else if (comp.getType() == STRING) { - if (comp.getString(ai).equals("false") && getBoolean() == false) + public boolean equals(AI ai, Object comp) throws LeekRunException { + if (comp instanceof ArrayLeekValue) { + return mValues.equals(ai, ((ArrayLeekValue) comp).mValues); + } else if (mValues.size() == 1) { // Si y'a un seul élément dans le tableau + var firstValue = mValues.getHeadElement().value(); + if (firstValue == null && comp == null) { + return ai.getVersion() == 1; // Bug in LS1, [null] == null + } + return ai.eq(firstValue, comp); + } else if (comp instanceof Boolean) { + return ai.bool(comp) == ai.bool(this); + } else if (comp instanceof String) { + if (ai.string(comp).equals("false") && ai.bool(this) == false) return true; - else if (comp.getString(ai).equals("true") && getBoolean() == true) + else if (ai.string(comp).equals("true") && ai.bool(this) == true) return true; - else if (comp.getString(ai).isEmpty() && mValues.size() == 0) + else if (ai.string(comp).isEmpty() && mValues.size() == 0) return true; - } else if (comp.getType() == NUMBER) { - if (mValues.size() == 0 && comp.getInt(ai) == 0) + } else if (comp instanceof Number) { + if (mValues.size() == 0 && ai.integer(comp) == 0) return true; } return false; } - @Override - public int getSize() throws LeekRunException { - return mValues.getSize(); - } - - @Override - public AbstractLeekValue add(AI ai, AbstractLeekValue value) throws Exception { - value = value.getValue(); + public Object add_eq(AI ai, Object value) throws LeekRunException { if (value instanceof ArrayLeekValue) { // mValues.reindex(ai); - ArrayIterator iterator = value.getArray().getArrayIterator(); + ArrayIterator iterator = ((ArrayLeekValue) value).getArrayIterator(); while (!iterator.ended()) { - if (iterator.key() instanceof String) - mValues.getOrCreate(ai, iterator.getKey(ai).getString(ai)).set(ai, iterator.getValue(ai)); + if (iterator.key() instanceof String || iterator.key() instanceof ObjectLeekValue) + mValues.getOrCreate(ai, ai.string(iterator.getKey(ai))).set(iterator.getValue(ai)); else mValues.push(ai, iterator.getValue(ai)); iterator.next(); } + } else { + mValues.push(ai, value); } return this; } @@ -271,35 +370,63 @@ public ArrayIterator getArrayIterator() { return new ArrayIterator(mValues.getHeadElement()); } - public Iterator getReversedIterator() { + public Iterator getReversedIterator() { return mValues.reversedIterator(); } - public void sort(AI ai, Comparator comparator) throws Exception { + public void sort(AI ai, Comparator comparator) throws LeekRunException { mValues.sort(ai, comparator); } - public void setParent(PhpArrayVariableLeekValue parent) { - mValues.setParent(parent); + public JSON toJSON(AI ai) throws LeekRunException { + return toJSON(ai, new HashSet<>()); } - @Override - public Object toJSON(AI ai) throws Exception { + public JSON toJSON(AI ai, HashSet visited) throws LeekRunException { + visited.add(this); if (mValues.isAssociative()) { JSONObject o = new JSONObject(); ArrayIterator i = getArrayIterator(); while (!i.ended()) { - o.put(i.key().toString(), i.getValue(ai).toJSON(ai)); + var v = i.getValue(ai); + if (!visited.contains(v)) { + if (!ai.isPrimitive(v)) { + visited.add(v); + } + o.put(i.key().toString(), ai.toJSON(v)); + } i.next(); } return o; } else { JSONArray a = new JSONArray(); - for (AbstractLeekValue v : this) { - a.add(v.toJSON(ai)); + for (var v : this) { + if (!visited.contains(v)) { + if (!ai.isPrimitive(v)) { + visited.add(v); + } + a.add(ai.toJSON(v.getValue())); + } } return a; } } + + @Override + public String toString() { + var r = "["; + boolean first = true; + ArrayIterator i = getArrayIterator(); + while (!i.ended()) { + if (first) first = false; + else r += ", "; + if (mValues.isAssociative()) { + r += i.key().toString() + ": "; + } + r += i.value(); + i.next(); + } + return r + "]"; + } } diff --git a/src/main/java/leekscript/runner/values/BooleanLeekValue.java b/src/main/java/leekscript/runner/values/BooleanLeekValue.java deleted file mode 100644 index a96e2987..00000000 --- a/src/main/java/leekscript/runner/values/BooleanLeekValue.java +++ /dev/null @@ -1,83 +0,0 @@ -package leekscript.runner.values; - -import leekscript.runner.AI; -import leekscript.runner.LeekOperations; -import leekscript.runner.LeekRunException; - -public class BooleanLeekValue extends AbstractLeekValue { - - public final boolean mValue; - - public BooleanLeekValue(boolean value) { - mValue = value; - } - - @Override - public int getSize() { - return 1; - } - - @Override - public boolean getBoolean() { - return mValue; - } - - @Override - public int getInt(AI ai) { - return mValue ? 1 : 0; - } - - @Override - public double getDouble(AI ai) { - return mValue ? 1 : 0; - } - - @Override - public boolean isNumeric() { - return true; - } - - @Override - public String getString(AI ai) { - return mValue ? "true" : "false"; - } - - @Override - public AbstractLeekValue add(AI ai, AbstractLeekValue val) throws Exception { - return LeekOperations.add(ai, this, val); - } - - @Override - public AbstractLeekValue multiply(AI ai, AbstractLeekValue val) throws Exception { - return LeekOperations.multiply(ai, this, val); - } - - @Override - public AbstractLeekValue divide(AI ai, AbstractLeekValue val) throws Exception { - return LeekOperations.divide(ai, this, val); - } - - @Override - public AbstractLeekValue modulus(AI ai, AbstractLeekValue val) throws Exception { - return LeekOperations.modulus(ai, this, val); - } - - @Override - public int getType() { - return BOOLEAN; - } - - @Override - public boolean equals(AI ai, AbstractLeekValue comp) throws LeekRunException { - if (comp.getType() == NULL) - return false; - else if (comp.getType() == ARRAY) - return comp.equals(ai, this); - return mValue == comp.getBoolean(); - } - - @Override - public Object toJSON(AI ai) { - return mValue; - } -} diff --git a/src/main/java/leekscript/runner/values/Box.java b/src/main/java/leekscript/runner/values/Box.java new file mode 100644 index 00000000..45761644 --- /dev/null +++ b/src/main/java/leekscript/runner/values/Box.java @@ -0,0 +1,228 @@ +package leekscript.runner.values; + +import leekscript.AILog; +import leekscript.runner.AI; +import leekscript.runner.LeekOperations; +import leekscript.runner.LeekRunException; +import leekscript.runner.LeekValueManager; +import leekscript.common.Error; + +public class Box { + + protected Object mValue; + protected AI mUAI = null; + + public Box(AI ai) throws LeekRunException { + mUAI = ai; + mValue = null; + } + + public Box(AI ai, Object value) throws LeekRunException { + mUAI = ai; + // System.out.println("ops Box"); + ai.ops(1); + if (ai.getVersion() >= 2) { + mValue = value; + } else if (value instanceof Box) { + mValue = LeekOperations.clone(ai, ((Box) value).getValue()); + } else { + mValue = value; + } + } + + public Box(AI ai, Object value, int ops) throws LeekRunException { + this(ai, value); + ai.ops(ops); + } + + public Object getValue() { + return mValue; + } + + public Object set(Object value) throws LeekRunException { + // mUAI.ops(1); + if (mUAI.getVersion() >= 2) { + return mValue = LeekValueManager.getValue(value); + } else { + if (value instanceof Box) { + return mValue = LeekOperations.clone(mUAI, ((Box) value).getValue()); + } else { + return mValue = value; + } + } + } + + public Object setRef(Object value) throws LeekRunException { + return mValue = LeekValueManager.getValue(value); + } + + public void initGlobal(Object value) throws LeekRunException { + if (value instanceof Box) { + if (mUAI.getVersion() >= 2) { + mValue = value; + } else { + mValue = LeekOperations.clone(mUAI, value); + } + } else { + mValue = value; + } + } + + public Object increment() throws LeekRunException { + if (mValue instanceof Integer) { + int value = (Integer) mValue; + mValue = value + 1; + return value; + } + if (mValue instanceof Double) { + double value = (Double) mValue; + mValue = value + 1; + return value; + } + mUAI.addSystemLog(AILog.ERROR, Error.INVALID_OPERATOR, new String[] { LeekValueManager.getString(mUAI, mValue), "++" }); + return null; + } + + public Object decrement() throws LeekRunException { + if (mValue instanceof Integer) { + int value = (Integer) mValue; + mValue = value - 1; + return value; + } + if (mValue instanceof Double) { + double value = (Double) mValue; + mValue = value - 1; + return value; + } + mUAI.addSystemLog(AILog.ERROR, Error.INVALID_OPERATOR, new String[] { LeekValueManager.getString(mUAI, mValue) + "--" }); + return null; + } + + public Object pre_increment() throws LeekRunException { + if (mValue instanceof Integer) { + return mValue = (Integer) mValue + 1; + } + if (mValue instanceof Double) { + return mValue = (Double) mValue + 1; + } + mUAI.addSystemLog(AILog.ERROR, Error.INVALID_OPERATOR, new String[] { "++" + LeekValueManager.getString(mUAI, mValue) }); + return null; + } + + public Object pre_decrement() throws LeekRunException { + if (mValue instanceof Integer) { + return mValue = (Integer) mValue - 1; + } + if (mValue instanceof Double) { + return mValue = (Double) mValue - 1; + } + mUAI.addSystemLog(AILog.ERROR, Error.INVALID_OPERATOR, new String[] { "--" + LeekValueManager.getString(mUAI, mValue) }); + return null; + } + + public Object not() throws LeekRunException { + // mUAI.ops(1); + return !mUAI.bool(mValue); + } + + public Object opposite() throws LeekRunException { + // mUAI.ops(1); + if (mValue instanceof Double) { + return -(Double) mValue; + } + return -mUAI.integer(mValue); + } + + public Object add_eq(Object val) throws LeekRunException { + if (mValue instanceof ArrayLeekValue && !(val instanceof String)) { + return mValue = mUAI.add_eq(mValue, val); + } + return mValue = mUAI.add(mValue, val); + } + + public Object sub_eq(Object val) throws LeekRunException { + return mValue = mUAI.sub(mValue, val); + } + + public Object mul_eq(Object val) throws LeekRunException { + return mValue = mUAI.mul(mValue, val); + } + + public Object pow_eq(Object val) throws LeekRunException { + return mValue = mUAI.pow(mValue, val); + } + + public int band_eq(Object val) throws LeekRunException { + return (int) (mValue = mUAI.band(mValue, val)); + } + + public int bor_eq(Object val) throws LeekRunException { + return (int) (mValue = mUAI.bor(mValue, val)); + } + + public int bxor_eq(Object val) throws LeekRunException { + return (int) (mValue = mUAI.bxor(mValue, val)); + } + + public int shl_eq(Object val) throws LeekRunException { + return (int) (mValue = mUAI.shl(mValue, val)); + } + + public int shr_eq(Object val) throws LeekRunException { + return (int) (mValue = mUAI.shr(mValue, val)); + } + + public int ushr_eq(Object val) throws LeekRunException { + return (int) (mValue = mUAI.ushr(mValue, val)); + } + + public Object div_eq(Object val) throws LeekRunException { + return mValue = mUAI.div(mValue, val); + } + + public Object mod_eq(Object val) throws LeekRunException { + return mValue = mUAI.mod(mValue, val); + } + + public Object get(Object index, ClassLeekValue fromClass) throws LeekRunException { + return mUAI.get(mValue, index, fromClass); + } + + public Box getOrCreate(Object index) throws LeekRunException { + return LeekValueManager.getOrCreate(mUAI, mValue, index); + } + + public Object getField(String field, ClassLeekValue fromClass) throws LeekRunException { + if (mValue instanceof ObjectLeekValue) { + return ((ObjectLeekValue) mValue).getField(field, fromClass); + } + return null; + } + + public Box getFieldL(String field) throws LeekRunException { + if (mValue instanceof ObjectLeekValue) { + return ((ObjectLeekValue) mValue).getFieldL(field); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object put(AI ai, Object key, Object value) throws LeekRunException { + if (mValue instanceof ArrayLeekValue) { + return ((ArrayLeekValue) mValue).put(ai, key, value); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public AI getAI() { + return mUAI; + } + + public Object execute(Object... arguments) throws LeekRunException { + return LeekValueManager.execute(mUAI, mValue, arguments); + } + + @Override + public String toString() { + return mValue != null ? mValue.toString() : "null"; + } +} diff --git a/src/main/java/leekscript/runner/values/ClassLeekValue.java b/src/main/java/leekscript/runner/values/ClassLeekValue.java new file mode 100644 index 00000000..85d892e9 --- /dev/null +++ b/src/main/java/leekscript/runner/values/ClassLeekValue.java @@ -0,0 +1,493 @@ +package leekscript.runner.values; + +import java.util.HashMap; +import java.util.LinkedHashMap; + +import leekscript.AILog; +import leekscript.runner.AI; +import leekscript.runner.LeekRunException; +import leekscript.runner.LeekAnonymousFunction; +import leekscript.common.AccessLevel; +import leekscript.runner.LeekFunction; +import leekscript.common.Error; + +public class ClassLeekValue extends FunctionLeekValue { + + public static class ClassField { + Object value; + AccessLevel level; + public ClassField(Object value, AccessLevel level) { + this.value = value; + this.level = level; + } + }; + + public static class ClassMethod { + LeekAnonymousFunction value; + AccessLevel level; + public ClassMethod(LeekAnonymousFunction value, AccessLevel level) { + this.value = value; + this.level = level; + } + }; + + public static class ClassStaticMethod { + LeekFunction value; + AccessLevel level; + public ClassStaticMethod(LeekFunction value, AccessLevel level) { + this.value = value; + this.level = level; + } + }; + + public AI ai; + public String name; + public ClassLeekValue parent; + public LinkedHashMap fields = new LinkedHashMap<>(); + public LinkedHashMap staticFields = new LinkedHashMap<>(); + public HashMap constructors = new HashMap<>(); + public HashMap methods = new HashMap<>(); + public HashMap genericMethods = new HashMap<>(); + public HashMap staticMethods = new HashMap<>(); + public HashMap genericStaticMethods = new HashMap<>(); + public LeekAnonymousFunction initFields = null; + + private ArrayLeekValue fieldsArray; + private ArrayLeekValue methodsArray; + + public ClassLeekValue(AI ai, String name) { + this(ai, name, null); + } + + public ClassLeekValue(AI ai, String name, ClassLeekValue parent) { + super(null, FunctionLeekValue.STATIC_METHOD, -1); + this.mAnonymous = new LeekAnonymousFunction() { + @Override + public Object run(ObjectLeekValue thiz, Object... values) throws LeekRunException { + return execute(values); + } + }; + this.ai = ai; + this.name = name; + this.parent = parent; + } + + public void setParent(ClassLeekValue parent) { + this.parent = parent; + } + + public void addConstructor(int arg_count, LeekAnonymousFunction function, AccessLevel level) { + constructors.put(arg_count, new ClassMethod(function, level)); + } + + public void addField(String field, AccessLevel level) { + fields.put(field, new ClassField(null, level)); + } + + public void addStaticField(AI ai, String field, Object value, AccessLevel level) throws LeekRunException { + staticFields.put(field, new ObjectVariableValue(ai, value, level)); + } + + public void addMethod(String method, int argCount, LeekAnonymousFunction function, AccessLevel level) { + methods.put(method + "_" + argCount, new ClassMethod(function, level)); + } + + public void addGenericMethod(String method) { + genericMethods.put(method, new FunctionLeekValue(new LeekAnonymousFunction() { + public Object run(ObjectLeekValue thiz, Object... arguments) throws LeekRunException { + final var methodCode = method + "_" + arguments.length; + final var m = methods.get(methodCode); + if (m != null) { + return m.value.run(thiz, arguments); + } + ai.addSystemLog(leekscript.AILog.ERROR, Error.UNKNOWN_METHOD, new String[] { name, createMethodError(methodCode) }); + return null; + } + }, FunctionLeekValue.METHOD, -1)); + } + + public void addStaticMethod(String method, int argCount, LeekFunction function, AccessLevel level) { + staticMethods.put(method + "_" + argCount, new ClassStaticMethod(function, level)); + } + + public void addGenericStaticMethod(String method) { + genericStaticMethods.put(method, new FunctionLeekValue(new LeekAnonymousFunction() { + public Object run(ObjectLeekValue thiz, Object... arguments) throws LeekRunException { + final var methodCode = method + "_" + arguments.length; + final var m = staticMethods.get(methodCode); + if (m != null) { + return m.value.run(null, arguments); + } + ai.addSystemLog(leekscript.AILog.ERROR, Error.UNKNOWN_METHOD, new String[] { name, createMethodError(methodCode) }); + return null; + } + }, FunctionLeekValue.STATIC_METHOD, -1)); + } + + public Object getField(String field) throws LeekRunException { + return getField(ai, field, this); + } + + public Object getField(AI ai, String field, ClassLeekValue fromClass) throws LeekRunException { + if (field.equals("fields")) { + return getFieldsArray(); + } else if (field.equals("staticFields")) { + return getStaticFieldsArray(); + } else if (field.equals("methods")) { + return getMethodsArray(); + } else if (field.equals("staticMethods")) { + return getStaticMethodsArray(); + } else if (field.equals("name")) { + return name; + } else if (field.equals("super")) { + return parent; + } + // Private + var result = staticFields.get(field); + if (result != null) { + if (fromClass == this) { + return result.getValue(); + } else { + // Protected : Access from descendant + if (fromClass != null && fromClass.descendsFrom(this)) { + if (result.level == AccessLevel.PRIVATE) { + ai.addSystemLog(AILog.ERROR, Error.PRIVATE_STATIC_FIELD, new String[] { this.name, field }); + return null; + } + return result.getValue(); + } else { + // Public : Access from outside + if (result.level != AccessLevel.PUBLIC) { + ai.addSystemLog(AILog.ERROR, result.level == AccessLevel.PROTECTED ? Error.PROTECTED_STATIC_FIELD : Error.PRIVATE_STATIC_FIELD, new String[] { this.name, field }); + return null; + } + return result.getValue(); + } + } + } + var generic = genericMethods.get(field); + if (generic != null) return generic; + generic = genericStaticMethods.get(field); + if (generic != null) return generic; + + if (parent instanceof ClassLeekValue) { + return parent.getField(ai, field, fromClass); + } + return null; + } + + public Box getFieldL(String field) throws LeekRunException { + Box result = staticFields.get(field); + if (result != null) { + return result; + } + if (parent instanceof ClassLeekValue) { + return parent.getFieldL(field); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object setField(String field, Object value) throws LeekRunException { + var result = staticFields.get(field); + if (result != null) { + return result.set(value); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object field_inc(String field) throws LeekRunException { + var result = getFieldL(field); + return result.increment(); + } + + public Object field_pre_inc(String field) throws LeekRunException { + var result = getFieldL(field); + return result.pre_increment(); + } + + public Object field_dec(String field) throws LeekRunException { + var result = getFieldL(field); + return result.decrement(); + } + + public Object field_pre_dec(String field) throws LeekRunException { + var result = getFieldL(field); + return result.pre_decrement(); + } + + public Object field_add_eq(String field, Object value) throws LeekRunException { + var result = getFieldL(field); + return result.add_eq(value); + } + + public Object field_sub_eq(String field, Object value) throws LeekRunException { + var result = getFieldL(field); + return result.sub_eq(value); + } + + public Object field_mul_eq(String field, Object value) throws LeekRunException { + var result = getFieldL(field); + return result.mul_eq(value); + } + + public Object field_pow_eq(String field, Object value) throws LeekRunException { + var result = getFieldL(field); + return result.pow_eq(value); + } + + public Object field_div_eq(String field, Object value) throws LeekRunException { + var result = getFieldL(field); + return result.div_eq(value); + } + + public Object field_mod_eq(String field, Object value) throws LeekRunException { + var result = getFieldL(field); + return result.mod_eq(value); + } + + public Object field_bor_eq(String field, Object value) throws LeekRunException { + var result = getFieldL(field); + return result.bor_eq(value); + } + + public Object field_bxor_eq(String field, Object value) throws LeekRunException { + var result = getFieldL(field); + return result.bxor_eq(value); + } + + public Object field_band_eq(String field, Object value) throws LeekRunException { + var result = getFieldL(field); + return result.band_eq(value); + } + + public Object field_shl_eq(String field, Object value) throws LeekRunException { + var result = getFieldL(field); + return result.shl_eq(value); + } + + public Object field_shr_eq(String field, Object value) throws LeekRunException { + var result = getFieldL(field); + return result.shr_eq(value); + } + + public Object field_ushr_eq(String field, Object value) throws LeekRunException { + var result = getFieldL(field); + return result.ushr_eq(value); + } + + public Object callMethod(String method, ClassLeekValue fromClass, Object... arguments) throws LeekRunException { + ai.ops(1); + var result = getStaticMethod(ai, method, fromClass); + if (result == null) { + ai.addSystemLog(AILog.ERROR, Error.UNKNOWN_STATIC_METHOD, new String[] { name, createMethodError(method) }); + return null; + } + + // Call method with new arguments, add the object at the beginning + return result.run(arguments); + } + + public Object callConstructor(ObjectLeekValue thiz, Object... arguments) throws LeekRunException { + if (!constructors.containsKey(arguments.length)) { + ai.addSystemLog(AILog.ERROR, Error.UNKNOWN_CONSTRUCTOR, new String[] { name, String.valueOf(arguments.length) }); + return thiz; + } + constructors.get(arguments.length).value.run(thiz, arguments); + return thiz; + } + + public static String createMethodError(String method) { + int underscore = method.lastIndexOf("_"); + int argCount = Integer.parseInt(method.substring(underscore + 1)); + String methodRealName = method.substring(0, underscore) + "("; + for (int i = 0; i < argCount; ++i) { + if (i > 0) methodRealName += ", "; + methodRealName += "x"; + } + methodRealName += ")"; + return methodRealName; + } + + /** + * Constructors + */ + public Object execute(Object... arguments) throws LeekRunException { + if (this == ai.valueClass || this == ai.jsonClass || this == ai.systemClass || this == ai.functionClass || this == ai.classClass) { + ai.addSystemLog(AILog.ERROR, Error.UNKNOWN_CONSTRUCTOR, new String[] { name, String.valueOf(arguments.length) }); + return null; + } + if (this == ai.nullClass) return null; + if (this == ai.booleanClass) return false; + if (this == ai.integerClass) return 0; + if (this == ai.realClass || this == ai.numberClass) return 0.0; + if (this == ai.stringClass) return ""; + if (this == ai.arrayClass) return new ArrayLeekValue(); + if (this == ai.objectClass) return new ObjectLeekValue(ai.objectClass); + + // Create the actual object + ai.ops(1); + ObjectLeekValue object = new ObjectLeekValue(this); + // Init fields + if (this.initFields != null) { + this.initFields.run(object); + } + + int arg_count = arguments.length; + if (constructors.containsKey(arg_count)) { + constructors.get(arg_count).value.run(object, arguments); + return object; + } else { + if (arg_count > 0) { + ai.addSystemLog(AILog.ERROR, Error.UNKNOWN_CONSTRUCTOR, new String[] { name, String.valueOf(arguments.length) }); + } + return object; + } + } + + public Object run(ObjectLeekValue thiz, Object... arguments) throws LeekRunException { + return execute(ai, arguments); + } + + private ArrayLeekValue getFieldsArray() throws LeekRunException { + if (fieldsArray == null) { + Object[] values = new Object[fields.size()]; + int i = 0; + for (var f : fields.entrySet()) { + values[i++] = f.getKey(); + } + fieldsArray = new ArrayLeekValue(ai, values); + } + return fieldsArray; + } + + private ArrayLeekValue getStaticFieldsArray() throws LeekRunException { + if (fieldsArray == null) { + Object[] values = new Object[staticFields.size()]; + int i = 0; + for (var f : staticFields.entrySet()) { + values[i++] = f.getKey(); + } + fieldsArray = new ArrayLeekValue(ai, values); + } + return fieldsArray; + } + + private ArrayLeekValue getMethodsArray() throws LeekRunException { + if (methodsArray == null) { + Object[] values = new Object[genericMethods.size()]; + int i = 0; + for (var f : genericMethods.entrySet()) { + values[i++] = f.getKey(); + } + methodsArray = new ArrayLeekValue(ai, values); + } + return methodsArray; + } + + private ArrayLeekValue getStaticMethodsArray() throws LeekRunException { + if (methodsArray == null) { + Object[] values = new Object[genericStaticMethods.size()]; + int i = 0; + for (var f : genericStaticMethods.entrySet()) { + values[i++] = f.getKey(); + } + methodsArray = new ArrayLeekValue(ai, values); + } + return methodsArray; + } + + public LeekAnonymousFunction getMethod(AI ai, String method, ClassLeekValue fromClass) throws LeekRunException { + var m = methods.get(method); + if (m != null) { + // Private : Access from same class + if (fromClass == this) { + return m.value; + } else { + // Protected : Access from descendant + if (fromClass != null && fromClass.descendsFrom(this)) { + if (m.level == AccessLevel.PRIVATE) { + ai.addSystemLog(AILog.ERROR, Error.PRIVATE_METHOD, new String[] { this.name, method }); + return null; + } + return m.value; + } else { + // Public : Access from outside + if (m.level != AccessLevel.PUBLIC) { + ai.addSystemLog(AILog.ERROR, m.level == AccessLevel.PROTECTED ? Error.PROTECTED_METHOD : Error.PRIVATE_METHOD, new String[] { this.name, method }); + return null; + } + return m.value; + } + } + } + if (parent instanceof ClassLeekValue) { + return ((ClassLeekValue) parent).getMethod(ai, method, fromClass); + } + return null; + } + + public LeekFunction getStaticMethod(AI ai, String method, ClassLeekValue fromClass) throws LeekRunException { + var m = staticMethods.get(method); + if (m != null) { + // Private : Access from same class + if (fromClass == this) { + return m.value; + } else { + // Protected : Access from descendant + if (fromClass != null && fromClass.descendsFrom(this)) { + if (m.level == AccessLevel.PRIVATE) { + ai.addSystemLog(AILog.ERROR, Error.PRIVATE_STATIC_METHOD, new String[] { this.name, method }); + return null; + } + return m.value; + } else { + // Public : Access from outside + if (m.level != AccessLevel.PUBLIC) { + ai.addSystemLog(AILog.ERROR, m.level == AccessLevel.PROTECTED ? Error.PROTECTED_STATIC_METHOD : Error.PRIVATE_STATIC_METHOD, new String[] { this.name, method }); + return null; + } + return m.value; + } + } + } + if (parent instanceof ClassLeekValue) { + return ((ClassLeekValue) parent).getStaticMethod(ai, method, fromClass); + } + return null; + } + + public LeekAnonymousFunction getSuperMethod(AI ai, String method, ClassLeekValue fromClass) throws LeekRunException { + if (parent instanceof ClassLeekValue) { + return ((ClassLeekValue) parent).getMethod(ai, method, fromClass); + } + return null; + } + + public String getString(AI ai) { + return ""; + } + + public boolean equals(AI ai, Object comp) throws LeekRunException { + if (comp instanceof ClassLeekValue) { + return ((ClassLeekValue) comp).name.equals(this.name); + } + return false; + } + + public Object toJSON(AI ai) { + return name; + } + + public boolean descendsFrom(ClassLeekValue clazz) { + var current = this; + while (current != null) { + if (current == clazz) return true; + if (current.parent instanceof ClassLeekValue) { + current = (ClassLeekValue) current.parent; + } else { + return false; + } + } + return false; + } +} diff --git a/src/main/java/leekscript/runner/values/DoubleLeekValue.java b/src/main/java/leekscript/runner/values/DoubleLeekValue.java deleted file mode 100644 index 02baae3b..00000000 --- a/src/main/java/leekscript/runner/values/DoubleLeekValue.java +++ /dev/null @@ -1,199 +0,0 @@ -package leekscript.runner.values; - -import java.text.DecimalFormat; - -import leekscript.runner.AI; -import leekscript.runner.LeekRunException; -import leekscript.runner.LeekValueManager; - -public class DoubleLeekValue extends AbstractLeekValue { - - private double mValue; - - public DoubleLeekValue(double value) { - mValue = value; - } - - @Override - public int getSize() { - return 2; - } - - @Override - public int getInt(AI ai) { - return (int) mValue; - } - - @Override - public double getDouble(AI ai) { - return mValue; - } - - @Override - public boolean getBoolean() { - return mValue != 0; - } - - @Override - public String getString(AI ai) throws LeekRunException { - ai.addOperations(3); - DecimalFormat df = new DecimalFormat(); - df.setMinimumFractionDigits(0); - return df.format(mValue); - } - - @Override - public boolean isNumeric() { - return true; - } - - @Override - public AbstractLeekValue increment(AI ai) throws LeekRunException { - ai.addOperations(ADD_COST); - mValue++; - return new DoubleLeekValue(mValue - 1d); - } - - @Override - public AbstractLeekValue decrement(AI ai) throws LeekRunException { - ai.addOperations(ADD_COST); - mValue--; - return new DoubleLeekValue(mValue + 1d); - } - - @Override - public AbstractLeekValue pre_increment(AI ai) throws LeekRunException { - ai.addOperations(ADD_COST); - mValue++; - return new DoubleLeekValue(mValue); - } - - @Override - public AbstractLeekValue pre_decrement(AI ai) throws LeekRunException { - ai.addOperations(ADD_COST); - mValue--; - return new DoubleLeekValue(mValue); - } - - @Override - public boolean less(AI ai, AbstractLeekValue comp) throws LeekRunException { - ai.addOperations(1); - return getDouble(ai) < comp.getDouble(ai); - } - - @Override - public boolean more(AI ai, AbstractLeekValue comp) throws LeekRunException { - ai.addOperations(1); - return getDouble(ai) > comp.getDouble(ai); - } - - @Override - public AbstractLeekValue add(AI ai, AbstractLeekValue val) throws LeekRunException { - ai.addOperations(ADD_COST); - mValue += val.getDouble(ai); - return this; - } - - @Override - public AbstractLeekValue minus(AI ai, AbstractLeekValue val) throws LeekRunException { - ai.addOperations(ADD_COST); - mValue -= val.getDouble(ai); - return this; - } - - @Override - public AbstractLeekValue multiply(AI ai, AbstractLeekValue val) throws LeekRunException { - ai.addOperations(MUL_COST); - mValue *= val.getDouble(ai); - return this; - } - - @Override - public AbstractLeekValue divide(AI ai, AbstractLeekValue val) throws LeekRunException { - ai.addOperations(DIV_COST); - mValue /= val.getDouble(ai); - return this; - } - - @Override - public AbstractLeekValue modulus(AI ai, AbstractLeekValue val) throws LeekRunException { - ai.addOperations(MOD_COST); - mValue %= val.getDouble(ai); - return this; - } - - @Override - public AbstractLeekValue opposite(AI ai) throws LeekRunException { - ai.addOperations(ADD_COST); - return new DoubleLeekValue(-getDouble(ai)); - } - - @Override - public AbstractLeekValue power(AI ai, AbstractLeekValue val) throws LeekRunException { - ai.addOperations(POW_COST); - mValue = Math.pow(mValue, val.getDouble(ai)); - return this; - } - - @Override - public AbstractLeekValue band(AI ai, AbstractLeekValue value) throws LeekRunException { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(getInt(ai) & value.getInt(ai)); - } - - @Override - public AbstractLeekValue bor(AI ai, AbstractLeekValue value) throws LeekRunException { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(getInt(ai) | value.getInt(ai)); - } - - @Override - public AbstractLeekValue bxor(AI ai, AbstractLeekValue value) throws LeekRunException { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(getInt(ai) ^ value.getInt(ai)); - } - - @Override - public AbstractLeekValue bleft(AI ai, AbstractLeekValue value) throws LeekRunException { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(getInt(ai) << value.getInt(ai)); - } - - @Override - public AbstractLeekValue bright(AI ai, AbstractLeekValue value) throws LeekRunException { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(getInt(ai) >> value.getInt(ai)); - } - - @Override - public AbstractLeekValue brotate(AI ai, AbstractLeekValue value) throws LeekRunException { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(getInt(ai) >>> value.getInt(ai)); - } - - @Override - public int getType() { - return NUMBER; - } - - @Override - public boolean equals(AI ai, AbstractLeekValue comp) throws LeekRunException { - if (comp.getType() == NUMBER) { - return comp.getDouble(ai) == mValue; - } else if (comp.getType() == BOOLEAN) { - return comp.getBoolean() == getBoolean(); - } else if (comp.getType() == STRING) { - if (comp.getString(ai).equals("true")) - return mValue != 0; - return mValue == comp.getDouble(ai); - } else if (comp.getType() == ARRAY) { - return comp.equals(ai, this); - } - return false; - } - - @Override - public Object toJSON(AI ai) { - return mValue; - } -} diff --git a/src/main/java/leekscript/runner/values/FunctionLeekValue.java b/src/main/java/leekscript/runner/values/FunctionLeekValue.java index 71780e96..8220407b 100644 --- a/src/main/java/leekscript/runner/values/FunctionLeekValue.java +++ b/src/main/java/leekscript/runner/values/FunctionLeekValue.java @@ -1,23 +1,29 @@ package leekscript.runner.values; +import java.util.Arrays; + import leekscript.AILog; import leekscript.runner.AI; import leekscript.runner.ILeekFunction; import leekscript.runner.LeekAnonymousFunction; -import leekscript.runner.LeekFunctions; import leekscript.runner.LeekOperations; +import leekscript.runner.LeekRunException; import leekscript.runner.LeekValueManager; +import leekscript.common.Error; -public class FunctionLeekValue extends AbstractLeekValue { +public class FunctionLeekValue { - private final static int LEEK_FUNCTION = 1; - private final static int USER_FUNCTION = 2; - private final static int ANONYMOUS_FUNCTION = 3; + public final static int LEEK_FUNCTION = 1; + public final static int USER_FUNCTION = 2; + public final static int ANONYMOUS_FUNCTION = 3; + public final static int METHOD = 4; + public final static int STATIC_METHOD = 5; private final int mType; private final int mId; private ILeekFunction mFunction; - private LeekAnonymousFunction mAnonymous; + protected LeekAnonymousFunction mAnonymous; + protected int mParametersCount = -1; public FunctionLeekValue(int id) { mType = USER_FUNCTION; @@ -36,9 +42,15 @@ public FunctionLeekValue(ILeekFunction fonction) { mId = 0; } - @Override - public boolean equals(AI ai, AbstractLeekValue comp) { - if (comp.getType() != getType()) { + public FunctionLeekValue(LeekAnonymousFunction fonction, int type, int parametersCount) { + mAnonymous = fonction; + mType = type; + mId = 0; + mParametersCount = parametersCount; + } + + public boolean equals(AI ai, Object comp) throws LeekRunException { + if (LeekValueManager.getType(comp) != LeekValue.FUNCTION) { return false; } if (!(comp instanceof FunctionLeekValue)) { @@ -54,15 +66,18 @@ public boolean equals(AI ai, AbstractLeekValue comp) { } } - @Override - public int getType() { - return AbstractLeekValue.FUNCTION; + private Object[] prepareValues(Object[] values, int count) { + Object[] retour = new Object[count]; + for (int i = 0; i < count; i++) { + retour[i] = (i >= values.length) ? null : LeekValueManager.getValue(values[i]); + } + return retour; } - private AbstractLeekValue[] copyValues(AI uai, AbstractLeekValue[] values, boolean[] references) throws Exception { - AbstractLeekValue[] copy = new AbstractLeekValue[values.length]; + private Object[] copyValues(AI uai, Object[] values, boolean[] references) throws LeekRunException { + Object[] copy = new Object[values.length]; for (int i = 0; i < values.length; i++) { - if (!references[i]) + if (!references[i] && values[i] instanceof Box) copy[i] = LeekOperations.clone(uai, values[i]); else copy[i] = values[i]; @@ -70,64 +85,63 @@ private AbstractLeekValue[] copyValues(AI uai, AbstractLeekValue[] values, boole return copy; } - private AbstractLeekValue[] prepareValues(AbstractLeekValue[] values, int count) { - AbstractLeekValue[] retour = new AbstractLeekValue[count]; - for (int i = 0; i < count; i++) { - retour[i] = (i >= values.length) ? LeekValueManager.NULL : values[i].getValue(); - } - return retour; - } - - @Override - public int getArgumentsCount(AI ai) throws Exception { + public int getArgumentsCount(AI ai) throws LeekRunException { if (mType == LEEK_FUNCTION) return mFunction.getArguments(); else if (mType == USER_FUNCTION) return ai.userFunctionCount(mId); else if (mType == ANONYMOUS_FUNCTION) return ai.anonymousFunctionCount(mId); - return -1; + return mParametersCount; } - @Override - public AbstractLeekValue executeFunction(AI ai, AbstractLeekValue[] values) throws Exception { + public Object execute(AI ai, Object... values) throws LeekRunException { if (mType == LEEK_FUNCTION) { - return LeekFunctions.executeFunction(ai, mFunction, prepareValues(values, mFunction.getArguments()), values.length); + return ai.sysexec(mFunction, prepareValues(values, mFunction.getArguments())); } else if (mType == USER_FUNCTION) { if (values.length != ai.userFunctionCount(mId)) { - - ai.addOperations(AI.ERROR_LOG_COST); - ai.addSystemLog(AILog.ERROR, AILog.CAN_NOT_EXECUTE_WITH_ARGUMENTS, new String[] { AbstractLeekValue.getParamString(values), String.valueOf(ai.userFunctionCount(mId)) }); + ai.addSystemLog(AILog.ERROR, Error.CAN_NOT_EXECUTE_WITH_ARGUMENTS, new String[] { LeekValue.getParamString(values), String.valueOf(ai.userFunctionCount(mId)) }); } - else - return ai.userFunctionExecute(mId, copyValues(ai, values, ai.userFunctionReference(mId))); - } - else if (mType == ANONYMOUS_FUNCTION) { + else { + if (ai.getVersion() >= 2) { + return ai.userFunctionExecute(mId, values); + } else { + return ai.userFunctionExecute(mId, copyValues(ai, values, ai.userFunctionReference(mId))); + } + } + } else if (mType == ANONYMOUS_FUNCTION) { if (values.length != ai.anonymousFunctionCount(mId)) { - - ai.addOperations(AI.ERROR_LOG_COST); - ai.addSystemLog(AILog.ERROR, AILog.CAN_NOT_EXECUTE_WITH_ARGUMENTS, - new String[] { AbstractLeekValue.getParamString(values), String.valueOf(ai.anonymousFunctionCount(mId)) }); + ai.addSystemLog(AILog.ERROR, Error.CAN_NOT_EXECUTE_WITH_ARGUMENTS, new String[] { LeekValue.getParamString(values), String.valueOf(ai.anonymousFunctionCount(mId)) }); + } else { + return mAnonymous.run(null, values); + } + } else if (mType == STATIC_METHOD) { + return mAnonymous.run(null, values); + } else if (mType == METHOD) { + if (values.length == 0) { + ai.addSystemLog(AILog.ERROR, Error.CAN_NOT_EXECUTE_WITH_ARGUMENTS, new String[] { LeekValue.getParamString(values), "1+" }); + } else if (!(values[0] instanceof ObjectLeekValue)) { + ai.addSystemLog(AILog.ERROR, Error.CAN_NOT_EXECUTE_WITH_ARGUMENTS, new String[] { LeekValue.getParamString(values), "object" }); + } else { + return mAnonymous.run((ObjectLeekValue) values[0], Arrays.copyOfRange(values, 1, values.length)); } - else - return mAnonymous.run(ai, copyValues(ai, values, ai.anonymousFunctionReference(mId))); - } - return LeekValueManager.NULL; + return null; } - @Override public String getString(AI ai) { if (mType == LEEK_FUNCTION) return "#Function " + mFunction; else if (mType == USER_FUNCTION) return "#User Function"; + else if (mType == METHOD) + return "#Method Function"; else return "#Anonymous Function"; } - public AbstractLeekValue cloneFunction() { + public FunctionLeekValue cloneFunction() { if (mType == LEEK_FUNCTION) return new FunctionLeekValue(mFunction); else if (mType == USER_FUNCTION) @@ -136,8 +150,11 @@ else if (mType == USER_FUNCTION) return new FunctionLeekValue(mId, mAnonymous); } - @Override public Object toJSON(AI ai) { return ""; } + + public String toString() { + return ""; + } } diff --git a/src/main/java/leekscript/runner/values/IntLeekValue.java b/src/main/java/leekscript/runner/values/IntLeekValue.java deleted file mode 100644 index 98a44a3d..00000000 --- a/src/main/java/leekscript/runner/values/IntLeekValue.java +++ /dev/null @@ -1,211 +0,0 @@ -package leekscript.runner.values; - -import leekscript.runner.AI; -import leekscript.runner.LeekOperations; -import leekscript.runner.LeekRunException; -import leekscript.runner.LeekValueManager; - -public class IntLeekValue extends AbstractLeekValue { - private int mValue; - - public IntLeekValue(int value) { - mValue = value; - } - - @Override - public void setInt(int nb) { - mValue = nb; - } - - @Override - public int getSize() { - return 1; - } - - @Override - public int getInt(AI ai) { - return mValue; - } - - @Override - public double getDouble(AI ai) { - return mValue; - } - - @Override - public boolean getBoolean() { - return mValue != 0; - } - - @Override - public String getString(AI ai) throws LeekRunException { - ai.addOperations(3); - return String.valueOf(mValue); - } - - @Override - public boolean isNumeric() { - return true; - } - - @Override - public AbstractLeekValue increment(AI ai) { - return this; - } - - @Override - public AbstractLeekValue decrement(AI ai) { - return this; - } - - @Override - public AbstractLeekValue pre_increment(AI ai) throws LeekRunException { - ai.addOperations(ADD_COST); - return LeekValueManager.getLeekIntValue(mValue + 1); - } - - @Override - public AbstractLeekValue pre_decrement(AI ai) throws LeekRunException { - ai.addOperations(ADD_COST); - return LeekValueManager.getLeekIntValue(mValue - 1); - } - - @Override - public boolean less(AI ai, AbstractLeekValue comp) throws LeekRunException { - ai.addOperations(1); - comp = comp.getValue(); - if (comp instanceof DoubleLeekValue) - return getDouble(ai) < comp.getDouble(ai); - return super.less(ai, comp); - } - - @Override - public boolean more(AI ai, AbstractLeekValue comp) throws LeekRunException { - ai.addOperations(1); - comp = comp.getValue(); - if (comp instanceof DoubleLeekValue) - return getDouble(ai) > comp.getDouble(ai); - return super.more(ai, comp); - } - - // Assign: - - @Override - public AbstractLeekValue add(AI ai, AbstractLeekValue val) throws LeekRunException { - ai.addOperations(ADD_COST); - val = val.getValue(); - if (val instanceof DoubleLeekValue) - return LeekValueManager.getLeekDoubleValue(mValue + val.getDouble(ai)); - return LeekValueManager.getLeekIntValue(ai, mValue + val.getInt(ai), this); - } - - @Override - public AbstractLeekValue minus(AI ai, AbstractLeekValue val) throws LeekRunException { - ai.addOperations(ADD_COST); - val = val.getValue(); - if (val instanceof DoubleLeekValue) - return LeekValueManager.getLeekDoubleValue(mValue - val.getDouble(ai)); - return LeekValueManager.getLeekIntValue(ai, mValue - val.getInt(ai), this); - } - - @Override - public AbstractLeekValue multiply(AI ai, AbstractLeekValue val) throws LeekRunException { - ai.addOperations(MUL_COST); - val = val.getValue(); - if (val instanceof DoubleLeekValue) - return LeekValueManager.getLeekDoubleValue(mValue * val.getDouble(ai)); - return LeekValueManager.getLeekIntValue(ai, mValue * val.getInt(ai), this); - } - - @Override - public AbstractLeekValue power(AI ai, AbstractLeekValue val) throws LeekRunException { - ai.addOperations(POW_COST); - val = val.getValue(); - if (val instanceof DoubleLeekValue) - return new DoubleLeekValue(Math.pow(mValue, val.getDouble(ai))); - return LeekValueManager.getLeekIntValue(ai, (int) Math.pow(mValue, val.getInt(ai)), this); - } - - @Override - public AbstractLeekValue band(AI ai, AbstractLeekValue value) throws LeekRunException { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(ai, mValue & value.getInt(ai), this); - } - - @Override - public AbstractLeekValue bor(AI ai, AbstractLeekValue value) throws LeekRunException { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(ai, mValue | value.getInt(ai), this); - } - - @Override - public AbstractLeekValue bxor(AI ai, AbstractLeekValue value) throws LeekRunException { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(ai, mValue ^ value.getInt(ai), this); - } - - @Override - public AbstractLeekValue bleft(AI ai, AbstractLeekValue value) throws LeekRunException { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(ai, mValue << value.getInt(ai), this); - } - - @Override - public AbstractLeekValue bright(AI ai, AbstractLeekValue value) throws LeekRunException { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(ai, mValue >> value.getInt(ai), this); - } - - @Override - public AbstractLeekValue brotate(AI ai, AbstractLeekValue value) throws LeekRunException { - ai.addOperations(1); - return LeekValueManager.getLeekIntValue(ai, mValue >>> value.getInt(ai), this); - } - - @Override - public AbstractLeekValue divide(AI ai, AbstractLeekValue val) throws Exception { - ai.addOperations(DIV_COST); - return LeekOperations.divide(ai, this, val); - } - - @Override - public AbstractLeekValue modulus(AI ai, AbstractLeekValue val) throws LeekRunException { - ai.addOperations(MOD_COST); - val = val.getValue(); - if (val instanceof DoubleLeekValue) - return LeekValueManager.getLeekDoubleValue(mValue % val.getDouble(ai)); - return LeekValueManager.getLeekIntValue(ai, mValue % val.getInt(ai), this); - } - - @Override - public int getType() { - return NUMBER; - } - - @Override - public boolean equals(AI ai, AbstractLeekValue comp) throws LeekRunException { - if (comp instanceof IntLeekValue) { - return comp.getInt(ai) == mValue; - } - else if (comp.getType() == NUMBER) { - return comp.getDouble(ai) == mValue; - } - else if (comp.getType() == BOOLEAN) { - return comp.getBoolean() == getBoolean(); - } - else if (comp.getType() == STRING) { - if (comp.getString(ai).equals("true")) - return mValue != 0; - return mValue == comp.getInt(ai); - } - else if (comp.getType() == ARRAY) { - return comp.equals(ai, this); - } - return false; - } - - @Override - public Object toJSON(AI ai) { - return mValue; - } -} diff --git a/src/main/java/leekscript/runner/values/LeekValue.java b/src/main/java/leekscript/runner/values/LeekValue.java new file mode 100644 index 00000000..be829014 --- /dev/null +++ b/src/main/java/leekscript/runner/values/LeekValue.java @@ -0,0 +1,54 @@ +package leekscript.runner.values; + +public class LeekValue { + + public final static int NUMBER_V1 = 1; + public final static int BOOLEAN_V1 = 2; + public final static int ARRAY_V1 = 3; + public final static int NULL_V1 = 4; + public final static int STRING_V1 = 5; + public final static int FUNCTION_V1 = 6; + public final static int CLASS_V1 = 7; + public final static int OBJECT_V1 = 8; + + public final static int NULL = 0; + public final static int NUMBER = 1; + public final static int BOOLEAN = 2; + public final static int STRING = 3; + public final static int ARRAY = 4; + public final static int FUNCTION = 5; + public final static int CLASS = 6; + public final static int OBJECT = 7; + + public final static int ADD_COST = 1; + public final static int MUL_COST = 5; + public final static int DIV_COST = 5; + public final static int MOD_COST = 5; + public final static int POW_COST = 140; + + public static String getParamString(Object[] parameters) { + String ret = ""; + for (int j = 0; j < parameters.length; j++) { + if (j != 0) + ret += ", "; + var v = parameters[j]; + if (v == null) + ret += "null"; + else if (v instanceof Number) + ret += "number"; + else if (v instanceof Boolean) + ret += "boolean"; + else if (v instanceof String) + ret += "string"; + else if (v instanceof ArrayLeekValue) + ret += "array"; + else if (v instanceof FunctionLeekValue) + ret += "function"; + else if (v instanceof ObjectLeekValue) + ret += "object"; + else + ret += "?"; + } + return ret; + } +} diff --git a/src/main/java/leekscript/runner/values/NullLeekValue.java b/src/main/java/leekscript/runner/values/NullLeekValue.java deleted file mode 100644 index ab2ba1a7..00000000 --- a/src/main/java/leekscript/runner/values/NullLeekValue.java +++ /dev/null @@ -1,61 +0,0 @@ -package leekscript.runner.values; - -import leekscript.runner.AI; -import leekscript.runner.LeekOperations; - -public class NullLeekValue extends AbstractLeekValue { - @Override - public boolean isNull() { - return true; - } - - @Override - public int getSize() { - return 1; - } - - @Override - public boolean isNumeric() { - return true; - } - - @Override - public String getString(AI ai) { - return "null"; - } - - @Override - public AbstractLeekValue add(AI ai, AbstractLeekValue val) throws Exception { - return LeekOperations.add(ai, this, val); - } - - @Override - public AbstractLeekValue multiply(AI ai, AbstractLeekValue val) throws Exception { - return LeekOperations.multiply(ai, this, val); - } - - @Override - public AbstractLeekValue divide(AI ai, AbstractLeekValue val) throws Exception { - return LeekOperations.divide(ai, this, val); - } - - @Override - public AbstractLeekValue modulus(AI ai, AbstractLeekValue val) throws Exception { - return LeekOperations.modulus(ai, this, val); - } - - @Override - public int getType() { - return NULL; - } - - @Override - public boolean equals(AI ai, AbstractLeekValue comp) { - return comp.getType() == NULL; - } - - @Override - public Object toJSON(AI ai) { - return null; - } -} diff --git a/src/main/java/leekscript/runner/values/ObjectLeekValue.java b/src/main/java/leekscript/runner/values/ObjectLeekValue.java new file mode 100644 index 00000000..7d0a7938 --- /dev/null +++ b/src/main/java/leekscript/runner/values/ObjectLeekValue.java @@ -0,0 +1,449 @@ +package leekscript.runner.values; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Set; + +import leekscript.AILog; +import leekscript.runner.AI; +import leekscript.runner.LeekOperations; +import leekscript.runner.LeekRunException; +import leekscript.runner.LeekValueManager; +import leekscript.common.AccessLevel; +import leekscript.common.Error; + +public class ObjectLeekValue { + + public final ClassLeekValue clazz; + public final LinkedHashMap fields = new LinkedHashMap<>(); + + public ObjectLeekValue(ClassLeekValue clazz) { + this.clazz = clazz; + } + + public ObjectLeekValue(AI ai, String[] keys, Object[] values) throws LeekRunException { + this.clazz = ai.objectClass; + for (int i = 0; i < keys.length; ++i) { + addField(ai, keys[i], values[i], AccessLevel.PUBLIC); + } + } + + public ObjectLeekValue(AI ai, ObjectLeekValue value, int level) throws LeekRunException { + this.clazz = value.clazz; + ai.ops(value.fields.size()); + for (var field : value.fields.entrySet()) { + if (level == 1) { + fields.put(field.getKey(), new ObjectVariableValue(ai, field.getValue().getValue(), field.getValue().level)); + } else { + fields.put(field.getKey(), new ObjectVariableValue(ai, LeekOperations.clone(ai, field.getValue().getValue(), level - 1), field.getValue().level)); + } + } + } + + public void addField(AI ai, String field, Object value, AccessLevel level) throws LeekRunException { + fields.put(field, new ObjectVariableValue(ai, LeekOperations.clone(ai, value), level)); + } + + public Object getField(String field) throws LeekRunException { + return getField(field, clazz); + } + + public Object getField(String field, ClassLeekValue fromClass) throws LeekRunException { + // System.out.println("getField " + field); + if (field.equals("class")) { + return clazz; + } + // Private : Access from same class + var result = fields.get(field); + if (result != null) { + if (fromClass == clazz || clazz.descendsFrom(fromClass)) { + return result.mValue; + } else { + // Protected : Access from descendant + if (fromClass != null && fromClass.descendsFrom(clazz)) { + if (result.level == AccessLevel.PRIVATE) { + clazz.ai.addSystemLog(AILog.ERROR, Error.PRIVATE_FIELD, new String[] { clazz.name, field }); + return null; + } + return result.mValue; + } else { + // Public : Access from outside + if (result.level != AccessLevel.PUBLIC) { + clazz.ai.addSystemLog(AILog.ERROR, result.level == AccessLevel.PROTECTED ? Error.PROTECTED_FIELD : Error.PRIVATE_FIELD, new String[] { clazz.name, field }); + return null; + } + return result.mValue; + } + } + } + var method = clazz.genericMethods.get(field); + if (method != null) return method; + + clazz.ai.addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { clazz.name, field }); + return null; + } + + public Box getFieldL(String field) throws LeekRunException { + // System.out.println("getField " + field); + var result = fields.get(field); + if (result != null) { + return result; + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + // ai.addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { clazz.name, field }); + // return null; + } + + public Object setField(String field, Object value) throws LeekRunException { + var result = fields.get(field); + // Pour un objet anonyme (classe Object), on peut rajouter des proprietés à la volée + if (result == null && clazz == clazz.ai.objectClass) { + addField(clazz.ai, field, value, AccessLevel.PUBLIC); + return value; + } + if (result != null) { + result.set(value); + return value; + } + clazz.ai.addSystemLog(AILog.ERROR, Error.UNKNOWN_FIELD, new String[] { clazz.name, field }); + return null; + } + + public Object field_inc(String field) throws LeekRunException { + var result = fields.get(field); + if (result != null) { + return result.increment(); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object field_pre_inc(String field) throws LeekRunException { + var result = fields.get(field); + if (result != null) { + return result.pre_increment(); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object field_dec(String field) throws LeekRunException { + var result = fields.get(field); + if (result != null) { + return result.decrement(); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object field_pre_dec(String field) throws LeekRunException { + var result = fields.get(field); + if (result != null) { + return result.pre_decrement(); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object field_add_eq(String field, Object value) throws LeekRunException { + var result = fields.get(field); + if (result != null) { + return result.add_eq(value); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object field_sub_eq(String field, Object value) throws LeekRunException { + var result = fields.get(field); + if (result != null) { + return result.sub_eq(value); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object field_mul_eq(String field, Object value) throws LeekRunException { + var result = fields.get(field); + if (result != null) { + return result.mul_eq(value); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object field_pow_eq(String field, Object value) throws LeekRunException { + var result = fields.get(field); + if (result != null) { + return result.pow_eq(value); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object field_div_eq(String field, Object value) throws LeekRunException { + var result = fields.get(field); + if (result != null) { + return result.div_eq(value); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object field_mod_eq(String field, Object value) throws LeekRunException { + var result = fields.get(field); + if (result != null) { + return result.mod_eq(value); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object field_bor_eq(String field, Object value) throws LeekRunException { + var result = fields.get(field); + if (result != null) { + return result.bor_eq(value); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object field_bxor_eq(String field, Object value) throws LeekRunException { + var result = fields.get(field); + if (result != null) { + return result.bxor_eq(value); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object field_band_eq(String field, Object value) throws LeekRunException { + var result = fields.get(field); + if (result != null) { + return result.band_eq(value); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object field_shl_eq(String field, Object value) throws LeekRunException { + var result = fields.get(field); + if (result != null) { + return result.shl_eq(value); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object field_shr_eq(String field, Object value) throws LeekRunException { + var result = fields.get(field); + if (result != null) { + return result.shr_eq(value); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Object field_ushr_eq(String field, Object value) throws LeekRunException { + var result = fields.get(field); + if (result != null) { + return result.ushr_eq(value); + } + throw new LeekRunException(LeekRunException.UNKNOWN_FIELD); + } + + public Box getOrCreate(AI ai, Object value) throws LeekRunException { + return getFieldL(LeekValueManager.getString(ai, value)); + } + + public Object callAccess(String field, String method, ClassLeekValue fromClass, Object... arguments) throws LeekRunException { + var resultM = clazz.getMethod(clazz.ai, method, fromClass); + if (resultM == null) { + if (method.equals("keys_0")) { + String[] values = new String[fields.size()]; + int i = 0; + for (var key : fields.keySet()) { + values[i++] = key; + } + return new ArrayLeekValue(clazz.ai, values); + } + if (method.equals("values_0")) { + Object[] values = new Object[fields.size()]; + int i = 0; + for (var value : fields.values()) { + values[i++] = value; + } + return new ArrayLeekValue(clazz.ai, values); + } + } + if (resultM == null) { + var result = fields.get(field); + if (result != null) { + // Private : Access from same class + if (result.level == AccessLevel.PRIVATE && fromClass != clazz) { + clazz.ai.addSystemLog(AILog.ERROR, Error.PRIVATE_FIELD, new String[] { clazz.name, field }); + return null; + } + // Protected : Access from descendant + if (result.level == AccessLevel.PROTECTED && (fromClass != clazz && !clazz.descendsFrom(fromClass))) { + clazz.ai.addSystemLog(AILog.ERROR, result.level == AccessLevel.PROTECTED ? Error.PROTECTED_FIELD : Error.PRIVATE_FIELD, new String[] { clazz.name, field }); + return null; + } + // Call the value + return clazz.ai.execute(result.mValue, arguments); + } + // Pas de méthode + int underscore = method.lastIndexOf("_"); + int argCount = Integer.parseInt(method.substring(underscore + 1)); + String methodRealName = method.substring(0, underscore) + "("; + for (int i = 0; i < argCount; ++i) { + if (i > 0) methodRealName += ", "; + methodRealName += "x"; + } + methodRealName += ")"; + clazz.ai.addSystemLog(AILog.ERROR, Error.UNKNOWN_METHOD, new String[] { clazz.name, methodRealName }); + return null; + } + + // Call method with new arguments, add the object at the beginning + return resultM.run(this, arguments); + } + + public Object callMethod(String method, ClassLeekValue fromClass, Object... arguments) throws LeekRunException { + var result = clazz.getMethod(clazz.ai, method, fromClass); + if (result == null) { + if (method.equals("keys_0")) { + String[] values = new String[fields.size()]; + int i = 0; + for (var key : fields.keySet()) { + values[i++] = key; + } + return new ArrayLeekValue(clazz.ai, values); + } + if (method.equals("values_0")) { + Object[] values = new Object[fields.size()]; + int i = 0; + for (var value : fields.values()) { + values[i++] = value; + } + return new ArrayLeekValue(clazz.ai, values); + } + } + if (result == null) { + int underscore = method.lastIndexOf("_"); + int argCount = Integer.parseInt(method.substring(underscore + 1)); + String methodRealName = method.substring(0, underscore) + "("; + for (int i = 0; i < argCount; ++i) { + if (i > 0) methodRealName += ", "; + methodRealName += "x"; + } + methodRealName += ")"; + clazz.ai.addSystemLog(AILog.ERROR, Error.UNKNOWN_METHOD, new String[] { clazz.name, methodRealName }); + return null; + } + // Call method with new arguments, add the object at the beginning + return result.run(this, arguments); + } + + public Object callSuperMethod(AI ai, String method, ClassLeekValue currentClass, Object... arguments) throws LeekRunException { + var result = currentClass.getSuperMethod(ai, method, currentClass); + if (result == null) { + int underscore = method.lastIndexOf("_"); + int argCount = Integer.parseInt(method.substring(underscore + 1)); + String methodRealName = method.substring(0, underscore) + "("; + for (int i = 0; i < argCount; ++i) { + if (i > 0) methodRealName += ", "; + methodRealName += "x"; + } + methodRealName += ")"; + ai.addSystemLog(AILog.ERROR, Error.UNKNOWN_METHOD, new String[] { clazz.name, methodRealName }); + return null; + } + // Call method with new arguments, add the object at the beginning + return result.run(this, arguments); + } + + public int getInt(AI ai) { + return fields.size(); + } + + public double getDouble(AI ai) { + return fields.size(); + } + + public String getString(AI ai, Set visited) throws LeekRunException { + + visited.add(this); + + var string_method = clazz.getMethod(ai, "string_0", null); + if (string_method != null) { + var result = string_method.run(this); + if (!(result instanceof String)) { + ai.addSystemLog(AILog.ERROR, Error.STRING_METHOD_MUST_RETURN_STRING, new String[] { clazz.name }); + } else { + return LeekValueManager.getString(ai, result, visited); + } + } + + ai.ops(1 + fields.size() * 2); + + var sb = new StringBuilder(); + if (clazz != clazz.ai.objectClass) { + sb.append(clazz.name).append(" "); + } + sb.append("{"); + boolean first = true; + for (HashMap.Entry field : fields.entrySet()) { + if (first) first = false; + else sb.append(", "); + sb.append(field.getKey()); + sb.append(": "); + if (visited.contains(field.getValue().getValue())) { + sb.append("<...>"); + } else { + if (!ai.isPrimitive(field.getValue().getValue())) { + visited.add(field.getValue().getValue()); + } + sb.append(ai.getString(field.getValue().getValue(), visited)); + } + } + sb.append("}"); + return sb.toString(); + } + + public boolean equals(AI ai, Object comp) throws LeekRunException { + if (comp instanceof ObjectLeekValue) { + return this == comp; + } + return false; + } + + public boolean equalsDeep(AI ai, Object comp) throws LeekRunException { + if (comp instanceof ObjectLeekValue) { + var o = (ObjectLeekValue) comp; + if (o.clazz != clazz) return false; + for (var f : fields.entrySet()) { + if (!ai.eq(f.getValue().getValue(), o.fields.get(f.getKey()))) { + return false; + } + } + return true; + } + return false; + } + + public Object toJSON(AI ai) { + return "object"; + } + + public ClassLeekValue getClazz() { + return clazz; + } + + public int size() { + return fields.size(); + } + + public String toString() { + var sb = new StringBuilder(); + if (clazz != clazz.ai.objectClass) { + sb.append(clazz.name).append(" "); + } + sb.append("{"); + boolean first = true; + for (HashMap.Entry field : fields.entrySet()) { + if (first) first = false; + else sb.append(", "); + sb.append(field.getKey()); + sb.append(": "); + sb.append(field.getValue().toString()); + } + sb.append("}"); + return sb.toString(); + } +} diff --git a/src/main/java/leekscript/runner/values/ObjectVariableValue.java b/src/main/java/leekscript/runner/values/ObjectVariableValue.java new file mode 100644 index 00000000..0bdc943d --- /dev/null +++ b/src/main/java/leekscript/runner/values/ObjectVariableValue.java @@ -0,0 +1,20 @@ +package leekscript.runner.values; + +import leekscript.common.AccessLevel; +import leekscript.runner.AI; +import leekscript.runner.LeekRunException; + +public class ObjectVariableValue extends Box { + + public AccessLevel level; + + public ObjectVariableValue(AI ai, AccessLevel level) throws LeekRunException { + super(ai); + this.level = level; + } + + public ObjectVariableValue(AI ai, Object value, AccessLevel level) throws LeekRunException { + super(ai, value); + this.level = level; + } +} diff --git a/src/main/java/leekscript/runner/values/PhpArrayVariableLeekValue.java b/src/main/java/leekscript/runner/values/PhpArrayVariableLeekValue.java deleted file mode 100644 index 78a6c501..00000000 --- a/src/main/java/leekscript/runner/values/PhpArrayVariableLeekValue.java +++ /dev/null @@ -1,42 +0,0 @@ -package leekscript.runner.values; - -import leekscript.runner.AI; -import leekscript.runner.LeekRunException; -import leekscript.runner.PhpArray; - -public class PhpArrayVariableLeekValue extends VariableLeekValue { - - private final PhpArray mArray; - private int mTotalSize = 0; - - public PhpArrayVariableLeekValue(PhpArray array, AI uai, AbstractLeekValue value, int keySize) throws Exception { - super(uai, value.getValue()); - mArray = array; - mTotalSize = value.getSize(); - mArray.updateArraySize(mTotalSize + keySize); - if (mValue instanceof ArrayLeekValue) { - mValue.getArray().setParent(this); - } - } - - @Override - public AbstractLeekValue set(AI ai, AbstractLeekValue value) throws Exception { - value = value.getValue(); - int size = value.getSize(); - mArray.updateArraySize(size - mTotalSize); - mTotalSize = size; - super.set(ai, value); - if (mValue instanceof ArrayLeekValue) - mValue.getArray().setParent(this); - return mValue; - } - - public void removeFromTable(int keySize) throws LeekRunException { - mArray.updateArraySize(-mTotalSize - keySize); - } - - public void updateSize(int delta) throws LeekRunException { - mTotalSize += delta; - mArray.updateArraySize(delta); - } -} diff --git a/src/main/java/leekscript/runner/values/ReferenceLeekValue.java b/src/main/java/leekscript/runner/values/ReferenceLeekValue.java deleted file mode 100644 index 442943c5..00000000 --- a/src/main/java/leekscript/runner/values/ReferenceLeekValue.java +++ /dev/null @@ -1,200 +0,0 @@ -package leekscript.runner.values; - -import leekscript.runner.AI; -import leekscript.runner.LeekOperations; -import leekscript.runner.LeekRunException; - -public class ReferenceLeekValue extends AbstractLeekValue { - - private AbstractLeekValue mValue; - - public ReferenceLeekValue(AI uai, AbstractLeekValue value) throws Exception { - if (!(value instanceof ReferenceLeekValue)) - mValue = value; - else - mValue = LeekOperations.clone(uai, value.getValue()); - } - - @Override - public int getSize() throws LeekRunException { - return mValue.getSize(); - } - - @Override - public int getInt(AI ai) throws LeekRunException { - return mValue.getInt(ai); - } - - @Override - public double getDouble(AI ai) throws LeekRunException { - return mValue.getDouble(ai); - } - - @Override - public String getString(AI ai) throws LeekRunException { - return mValue.getString(ai); - } - - @Override - public boolean getBoolean() { - return mValue.getBoolean(); - } - - @Override - public ArrayLeekValue getArray() { - return mValue.getArray(); - } - - @Override - public boolean isArray() { - return mValue.isArray(); - } - - @Override - public boolean isNull() { - return mValue.isNull(); - } - - // Fonctions spéciales L-Values - @Override - public AbstractLeekValue getValue() { - return mValue.getValue(); - } - - @Override - public AbstractLeekValue increment(AI ai) throws Exception { - return mValue.increment(ai); - } - - @Override - public AbstractLeekValue decrement(AI ai) throws Exception { - return mValue.decrement(ai); - } - - @Override - public AbstractLeekValue pre_increment(AI ai) throws Exception { - return mValue.pre_increment(ai); - } - - @Override - public AbstractLeekValue pre_decrement(AI ai) throws Exception { - return mValue.pre_decrement(ai); - } - - @Override - public AbstractLeekValue opposite(AI ai) throws Exception { - return mValue.opposite(ai); - } - - @Override - public boolean equals(AI ai, AbstractLeekValue comp) throws LeekRunException { - return mValue.equals(ai, comp); - } - - @Override - public boolean less(AI ai, AbstractLeekValue comp) throws LeekRunException { - return mValue.less(ai, comp); - } - - @Override - public boolean more(AI ai, AbstractLeekValue comp) throws LeekRunException { - return mValue.more(ai, comp); - } - - @Override - public AbstractLeekValue add(AI ai, AbstractLeekValue val) throws Exception { - - return mValue = mValue.add(ai, val); - } - - @Override - public AbstractLeekValue minus(AI ai, AbstractLeekValue val) throws Exception { - - return mValue = mValue.minus(ai, val); - } - - @Override - public AbstractLeekValue multiply(AI ai, AbstractLeekValue val) throws Exception { - - return mValue = mValue.multiply(ai, val); - } - - @Override - public AbstractLeekValue power(AI ai, AbstractLeekValue val) throws Exception { - - return mValue = mValue.power(ai, val); - } - - @Override - public AbstractLeekValue band(AI ai, AbstractLeekValue val) throws Exception { - - return mValue = mValue.band(ai, val); - } - - @Override - public AbstractLeekValue bor(AI ai, AbstractLeekValue val) throws Exception { - - return mValue = mValue.bor(ai, val); - } - - @Override - public AbstractLeekValue bxor(AI ai, AbstractLeekValue val) throws Exception { - - return mValue = mValue.bxor(ai, val); - } - - @Override - public AbstractLeekValue bleft(AI ai, AbstractLeekValue val) throws Exception { - - return mValue = mValue.bleft(ai, val); - } - - @Override - public AbstractLeekValue bright(AI ai, AbstractLeekValue val) throws Exception { - - return mValue = mValue.bright(ai, val); - } - - @Override - public AbstractLeekValue brotate(AI ai, AbstractLeekValue val) throws Exception { - - return mValue = mValue.brotate(ai, val); - } - - @Override - public AbstractLeekValue divide(AI ai, AbstractLeekValue val) throws Exception { - - return mValue = mValue.divide(ai, val); - } - - @Override - public AbstractLeekValue modulus(AI ai, AbstractLeekValue val) throws Exception { - - return mValue = mValue.modulus(ai, val); - } - - @Override - public int getType() { - return mValue.getType(); - } - - @Override - public boolean isReference() { - return true; - } - - @Override - public AbstractLeekValue executeFunction(AI ai, AbstractLeekValue[] values) throws Exception { - return mValue.executeFunction(ai, values); - } - - @Override - public int getArgumentsCount(AI ai) throws Exception { - return mValue.getArgumentsCount(ai); - } - - @Override - public Object toJSON(AI ai) throws Exception { - return mValue.toJSON(ai); - } -} diff --git a/src/main/java/leekscript/runner/values/StringLeekValue.java b/src/main/java/leekscript/runner/values/StringLeekValue.java deleted file mode 100644 index e5f954f4..00000000 --- a/src/main/java/leekscript/runner/values/StringLeekValue.java +++ /dev/null @@ -1,107 +0,0 @@ -package leekscript.runner.values; - -import leekscript.runner.AI; -import leekscript.runner.LeekRunException; - -public class StringLeekValue extends AbstractLeekValue { - - private String mValue; - - public StringLeekValue(String value) { - mValue = value; - } - - @Override - public int getSize() { - if (mValue == null) { - return 0; - } - return mValue.length(); - } - - @Override - public String getString(AI ai) { - return mValue; - } - - @Override - public boolean getBoolean() { - if (mValue.equals("false") || mValue.equals("0")) { - return false; - } - return !mValue.isEmpty(); - } - - @Override - public int getInt(AI ai) throws LeekRunException { - ai.addOperations(2); - if (mValue.isEmpty()) - return 0; - if (mValue.equals("true")) - return 1; - if (mValue.equals("false")) - return 0; - ai.addOperations(mValue.length()); - try { - return Integer.parseInt(mValue); - } catch (Exception e) { - return 1; - } - } - - @Override - public double getDouble(AI ai) throws LeekRunException { - ai.addOperations(2); - if (mValue.equals("true")) - return 1; - if (mValue.equals("false")) - return 0; - if (mValue.isEmpty()) - return 0; - ai.addOperations(mValue.length()); - try { - return Double.parseDouble(mValue); - } catch (Exception e) { - return 1; - } - } - - @Override - public AbstractLeekValue add(AI ai, AbstractLeekValue val) throws LeekRunException { - String s = val.getString(ai); - ai.addOperations(1 + s.length() + mValue.length()); - mValue += s; - return this; - } - - @Override - public int getType() { - return STRING; - } - - @Override - public boolean equals(AI ai, AbstractLeekValue comp) throws LeekRunException { - if (comp.getType() == NUMBER) { - if (mValue.equals("true")) - return comp.getDouble(ai) != 0; - if (comp instanceof IntLeekValue) - return getInt(ai) == comp.getInt(ai); - else - return getDouble(ai) == comp.getDouble(ai); - } else if (comp.getType() == BOOLEAN) { - return getBoolean() == comp.getBoolean(); - } else if (comp.getType() == STRING) { - String s = comp.getString(ai); - ai.addOperations(Math.min(s.length(), mValue.length())); - return mValue.equals(s); - } else if (comp.getType() == ARRAY) { - return comp.equals(ai, this); - } - return false; - } - - @Override - public Object toJSON(AI ai) { - return mValue; - } -} diff --git a/src/main/java/leekscript/runner/values/VariableLeekValue.java b/src/main/java/leekscript/runner/values/VariableLeekValue.java deleted file mode 100644 index ae4d611e..00000000 --- a/src/main/java/leekscript/runner/values/VariableLeekValue.java +++ /dev/null @@ -1,231 +0,0 @@ -package leekscript.runner.values; - -import leekscript.runner.AI; -import leekscript.runner.LeekOperations; -import leekscript.runner.LeekRunException; -import leekscript.runner.LeekValueManager; - -public class VariableLeekValue extends AbstractLeekValue { - - protected AbstractLeekValue mValue; - protected AI mUAI = null; - - public VariableLeekValue(AI uai, AbstractLeekValue value) throws Exception { - mUAI = uai; - mUAI.addOperations(1); - if (!(value instanceof VariableLeekValue)) - mValue = value.getValue(); - else { - if (value.isReference()) - mValue = value.getValue(); - else - mValue = LeekOperations.clone(uai, value.getValue()); - } - } - - @Override - public int getSize() throws LeekRunException { - return mValue.getSize(); - } - - @Override - public int getInt(AI ai) throws LeekRunException { - return mValue.getInt(ai); - } - - @Override - public double getDouble(AI ai) throws LeekRunException { - return mValue.getDouble(ai); - } - - @Override - public String getString(AI ai) throws LeekRunException { - return mValue.getString(ai); - } - - @Override - public boolean getBoolean() { - return mValue.getBoolean(); - } - - @Override - public ArrayLeekValue getArray() { - return mValue.getArray(); - } - - @Override - public boolean isArray() { - return mValue.isArray(); - } - - @Override - public boolean isNull() { - return mValue.isNull(); - } - - // Fonctions spéciales L-Values - @Override - public AbstractLeekValue getValue() { - return mValue; - } - - @Override - public AbstractLeekValue set(AI ai, AbstractLeekValue value) throws Exception { - ai.addOperations(1); - if (value.isReference()) - return mValue = value.getValue(); - else - return mValue = LeekOperations.clone(ai, value.getValue()); - } - - @Override - public AbstractLeekValue increment(AI ai) throws Exception { - mValue = mValue.getValue(); - if (mValue instanceof IntLeekValue) { - ai.addOperations(1); - int value = mValue.getInt(ai); - mValue = LeekValueManager.getLeekIntValue(ai, value + 1, mValue); - return LeekValueManager.getLeekIntValue(value); - } else - return mValue.increment(ai); - } - - @Override - public AbstractLeekValue decrement(AI ai) throws Exception { - mValue = mValue.getValue(); - if (mValue instanceof IntLeekValue) { - ai.addOperations(1); - int value = mValue.getInt(ai); - mValue = LeekValueManager.getLeekIntValue(ai, value - 1, mValue); - return LeekValueManager.getLeekIntValue(value); - } else - return mValue.decrement(ai); - } - - @Override - public AbstractLeekValue pre_increment(AI ai) throws Exception { - mValue = mValue.getValue(); - if (mValue instanceof IntLeekValue) { - ai.addOperations(1); - int value = mValue.getInt(ai); - return mValue = LeekValueManager.getLeekIntValue(ai, value + 1, mValue); - } else - return mValue.pre_increment(ai); - } - - @Override - public AbstractLeekValue pre_decrement(AI ai) throws Exception { - mValue = mValue.getValue(); - if (mValue instanceof IntLeekValue) { - ai.addOperations(1); - int value = mValue.getInt(ai); - return mValue = LeekValueManager.getLeekIntValue(ai, value - 1, mValue); - } else - return mValue.pre_decrement(ai); - } - - @Override - public AbstractLeekValue opposite(AI ai) throws Exception { - return mValue.opposite(ai); - } - - @Override - public boolean equals(AI ai, AbstractLeekValue comp) throws LeekRunException { - return mValue.equals(ai, comp); - } - - @Override - public boolean less(AI ai, AbstractLeekValue comp) throws LeekRunException { - return mValue.less(ai, comp); - } - - @Override - public boolean more(AI ai, AbstractLeekValue comp) throws LeekRunException { - return mValue.more(ai, comp); - } - - @Override - public AbstractLeekValue add(AI ai, AbstractLeekValue val) throws Exception { - return mValue = mValue.add(ai, val); - } - - @Override - public AbstractLeekValue minus(AI ai, AbstractLeekValue val) throws Exception { - return mValue = mValue.minus(ai, val); - } - - @Override - public AbstractLeekValue multiply(AI ai, AbstractLeekValue val) throws Exception { - return mValue = mValue.multiply(ai, val); - } - - @Override - public AbstractLeekValue power(AI ai, AbstractLeekValue val) throws Exception { - return mValue = mValue.power(ai, val); - } - - @Override - public AbstractLeekValue band(AI ai, AbstractLeekValue val) throws Exception { - return mValue = mValue.band(ai, val); - } - - @Override - public AbstractLeekValue bor(AI ai, AbstractLeekValue val) throws Exception { - return mValue = mValue.bor(ai, val); - } - - @Override - public AbstractLeekValue bxor(AI ai, AbstractLeekValue val) throws Exception { - return mValue = mValue.bxor(ai, val); - } - - @Override - public AbstractLeekValue bleft(AI ai, AbstractLeekValue val) throws Exception { - return mValue = mValue.bleft(ai, val); - } - - @Override - public AbstractLeekValue bright(AI ai, AbstractLeekValue val) throws Exception { - return mValue = mValue.bright(ai, val); - } - - @Override - public AbstractLeekValue brotate(AI ai, AbstractLeekValue val) throws Exception { - return mValue = mValue.brotate(ai, val); - } - - @Override - public AbstractLeekValue divide(AI ai, AbstractLeekValue val) throws Exception { - return mValue = mValue.divide(ai, val); - } - - @Override - public AbstractLeekValue modulus(AI ai, AbstractLeekValue val) throws Exception { - return mValue = mValue.modulus(ai, val); - } - - @Override - public int getType() { - return mValue.getType(); - } - - @Override - public boolean isReference() { - return mValue.isReference(); - } - - @Override - public AbstractLeekValue executeFunction(AI ai, AbstractLeekValue[] values) throws Exception { - return mValue.executeFunction(ai, values); - } - - @Override - public int getArgumentsCount(AI ai) throws Exception { - return mValue.getArgumentsCount(ai); - } - - @Override - public Object toJSON(AI ai) throws Exception { - return mValue.toJSON(ai); - } -} diff --git a/src/test/java/test/TestAI.java b/src/test/java/test/TestAI.java index e5ceeca1..b8ec76e6 100644 --- a/src/test/java/test/TestAI.java +++ b/src/test/java/test/TestAI.java @@ -1,59 +1,56 @@ package test; + import leekscript.runner.AI; -import leekscript.runner.values.AbstractLeekValue; +import leekscript.runner.LeekRunException; public class TestAI extends AI { public TestAI() throws Exception { - super(); - } - - @Override - protected String getErrorString() { - // TODO Auto-generated method stub - return null; + super(0, 2); } @Override - protected String getAItring() { - // TODO Auto-generated method stub + protected String getAIString() { return null; } @Override - public AbstractLeekValue runIA() throws Exception { - // TODO Auto-generated method stub + public Object runIA() throws LeekRunException { return null; } @Override public int userFunctionCount(int id) { - // TODO Auto-generated method stub return 0; } @Override public boolean[] userFunctionReference(int id) { - // TODO Auto-generated method stub return null; } @Override - public AbstractLeekValue userFunctionExecute(int id, AbstractLeekValue[] value) throws Exception { - // TODO Auto-generated method stub + public Object userFunctionExecute(int id, Object[] value) throws LeekRunException { return null; } @Override public int anonymousFunctionCount(int id) { - // TODO Auto-generated method stub return 0; } @Override public boolean[] anonymousFunctionReference(int id) { - // TODO Auto-generated method stub return null; } + @Override + public int getVersion() { + return 2; + } + + @Override + protected String[] getErrorFiles() { + return null; + } } diff --git a/src/test/java/test/TestAdvancedFunctions.java b/src/test/java/test/TestAdvancedFunctions.java deleted file mode 100644 index f8c8dbdb..00000000 --- a/src/test/java/test/TestAdvancedFunctions.java +++ /dev/null @@ -1,375 +0,0 @@ -package test; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import leekscript.LSException; -import leekscript.compiler.LeekScript; -import leekscript.compiler.exceptions.LeekCompilerException; -import leekscript.runner.AI; -import leekscript.runner.values.AbstractLeekValue; -import leekscript.runner.values.ArrayLeekValue; -import leekscript.runner.values.BooleanLeekValue; -import leekscript.runner.values.DoubleLeekValue; -import leekscript.runner.values.IntLeekValue; -import leekscript.runner.values.NullLeekValue; -import leekscript.runner.values.StringLeekValue; - -public class TestAdvancedFunctions { - - private AI ai; - - @Before - public void init() throws Exception { - ai = new TestAI(); - } - - // Test de retour sur des structures du leekscript - @Test - public void whileReturnTest() throws Exception { - // Test AI - Assert.assertTrue(LeekScript.testScript("var t = 0; while(t<5){ t++; return t;}", new IntLeekValue(1))); - Assert.assertTrue(LeekScript.testScript("var t = 0; while(t<5){ t++; return t;} return 0;", new IntLeekValue(1))); - } - - @Test - public void doWhileReturnTest() throws Exception { - // Test AI - Assert.assertTrue(LeekScript.testScript("var t = 0; do{ t++; return t;}while(t<5);", new IntLeekValue(1))); - try { - LeekScript.testScript("var t = 0; do{ t++; return t;}while(t<5); return 2;", new IntLeekValue(1)); - Assert.fail("Compilation validée..."); - } catch (LeekCompilerException e) { - return; - } catch (Exception e) { - Assert.fail("Compilation validée..."); - } - } - - @Test - public void forReturnTest() throws Exception { - // Test AI - Assert.assertTrue(LeekScript.testScript("for(var i=0;i<3;i++){ return i; }", new IntLeekValue(0))); - Assert.assertTrue(LeekScript.testScript("for(var i=0;i<3;i++){ return i; } return 2;", new IntLeekValue(0))); - } - - @Test - public void anonymousTest() throws Exception { - // Test AI - Assert.assertTrue(LeekScript.testScript("function te(a){ return function(){ return a**2; }; } return te(2)();", new IntLeekValue(4))); - } - - @Test - public void anonymous2Test() throws Exception { - // Test AI - Assert.assertTrue(LeekScript.testScript("function te(a){ return function(b){ return function(c){return a*b*c;}; }; } return te(2)(1)(2);", new IntLeekValue(4))); - } - - @Test - public void anonymous3Test() throws Exception { - // Test AI - Assert.assertTrue(LeekScript.testScript("var tab = [2,3,4,5,6];var r = [];for(var i: var j in tab){ r[i] = function(){ return j; };}return 4;", new IntLeekValue(4))); - } - - @Test - public void conditionalTest() throws Exception { - // Test AI - Assert.assertTrue(LeekScript.testScript("var test = 0; if(false) if(true) test = 3; else test = 1; return test;", new IntLeekValue(0))); - } - - @Test - public void forEachReturnTest() throws Exception { - // Test AI - Assert.assertTrue(LeekScript.testScript("var tab = [0,1,2,3]; for(var i in tab){ return i; }", new IntLeekValue(0))); - Assert.assertTrue(LeekScript.testScript("var tab = [0,1,2,3]; for(var i in tab){ return i; } return 5;", new IntLeekValue(0))); - } - - @Test - public void forEachKeyReturnTest() throws Exception { - // Test AI - Assert.assertTrue(LeekScript.testScript("var tab = [1:0,2:1,3:2,4:3]; for(var i : var j in tab){ return i; } ", new IntLeekValue(1))); - Assert.assertTrue(LeekScript.testScript("var tab = [1:0,2:1,3:2,4:3]; for(var i : var j in tab){ return i; } return 0;", new IntLeekValue(1))); - } - - @Test - public void divisionByZeroTest() throws Exception { - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - // Test nombre - codes.add("8/0"); - values.add(null); - - // Test nombre - codes.add("8/null"); - values.add(null); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - - - - @Test - public void arrayMapTest() throws Exception { - - // On lance le test - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("arrayMap([1,2,3,4,5],function(e){ return e*2; })"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { - new IntLeekValue(2), new IntLeekValue(4), new IntLeekValue(6), new IntLeekValue(8), new IntLeekValue(10) })); - - codes.add("arrayMap([4,9,16],sqrt)"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { - new IntLeekValue(2), new IntLeekValue(3), new IntLeekValue(4) })); - - codes.add("arrayMap(['a':1,'b':2],function(k,v){ return k+v;})"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { - new StringLeekValue("a"), new StringLeekValue("a1"), - new StringLeekValue("b"), new StringLeekValue("b2") }, true)); - - codes.add("function(){ var t = ['a':1,'b':2]; arrayMap(t,function(@k,@v){ v='tomate';k='ctus'; return 3;}); return t;}()"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { new StringLeekValue("a"), - new StringLeekValue("tomate"), - new StringLeekValue("b"), - new StringLeekValue("tomate") }, true)); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void arrayFilterTest() throws Exception { - - // On lance le test - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("arrayFilter([1,2,3,4,5,6,7,8,9],function(e){ return e>5; })"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { - new IntLeekValue(5), new IntLeekValue(6), new IntLeekValue(6), - new IntLeekValue(7), new IntLeekValue(7), - new IntLeekValue(8), new IntLeekValue(8), new IntLeekValue(9) }, true)); - - codes.add("arrayFilter([4,5,6,'test',8,9],function(e){ return e=='test'; })"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(3), new StringLeekValue("test") }, true)); - - codes.add("string(arrayFilter(['a','b','c','d'],function(k,v){ return k==3; }))"); - values.add("[3 : d]"); - - codes.add("string(function(){ var t = ['a','b','c','d']; arrayFilter(t,function(k,@v){ v=4; return k==3; }); return t;}())"); - values.add("[4, 4, 4, 4]"); - - codes.add("string(arrayFilter(['a','b','c','d'],function(k,@v){ v=4; return k==3; }))"); - values.add("[3 : 4]"); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void arrayFlatten() throws Exception { - - // On lance le test - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("arrayFlatten([6,7,[8,9]],99)"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { - new IntLeekValue(6), new IntLeekValue(7), new IntLeekValue(8), new IntLeekValue(9) })); - - codes.add("arrayFlatten([6,[[7]],[8,9]],2)"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { - new IntLeekValue(6), new IntLeekValue(7), new IntLeekValue(8), new IntLeekValue(9) })); - - codes.add("arrayFlatten([6,[[7]],[8,9]])"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { - new IntLeekValue(6), new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(7) }), - new IntLeekValue(8), new IntLeekValue(9) })); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void arrayFoldLeft() throws Exception { - - // On lance le test - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("arrayFoldLeft([6,7,8,9], function(a,b){return a+b;},0)"); - values.add(30); - - codes.add("arrayFoldLeft([1,0,1,2,5,7,9], function(a,b){return a+','+b;},'')"); - values.add(",1,0,1,2,5,7,9"); - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void arrayFoldRight() throws Exception { - - // On lance le test - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("arrayFoldRight([6,7,8,9], function(a,b){return a+b;},0)"); - values.add(30); - - codes.add("arrayFoldRight([1,0,1,2,5,7,9], function(a,b){return a+','+b;},'')"); - values.add("1,0,1,2,5,7,9,"); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void arrayPartition() throws Exception { - - // On lance le test - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("arrayPartition([6,7,8,9], function(a){return a&1;})"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { - new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(1), new IntLeekValue(7), new IntLeekValue(3), new IntLeekValue(9) }, true), - new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(0), new IntLeekValue(6), new IntLeekValue(2), new IntLeekValue(8) }, true) })); - - codes.add("string(arrayPartition([6,7,8,9], function(k,v){return k;}))"); - values.add("[[1 : 7, 2 : 8, 3 : 9], [6]]"); - - codes.add("string(arrayPartition([4,3,2,1], function(k,v){return k codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("string([0]+[1,2])"); - values.add("[0, 1, 2]"); - - codes.add("function(){ var a = [0,1]; a+= [3]; return string(a);}()"); - values.add("[0, 1, 3]"); - - codes.add("string(arrayConcat([0],[1,2]))"); - values.add("[0, 1, 2]"); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void arrayIter() throws Exception { - - // On lance le test - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("string(function(){ var t = [1,2,3,4]; arrayIter(t, function(v){ v=2; }); return t; }())"); - values.add("[1, 2, 3, 4]"); - - codes.add("string(function(){ var t = [1,2,3,4]; arrayIter(t, function(@v){ v=2; }); return t; }())"); - values.add("[2, 2, 2, 2]"); - - codes.add("string(function(){ var t = [1,2,3,4]; arrayIter(t, function(k, @v){ v=k; }); return t; }())"); - values.add("[0, 1, 2, 3]"); - - codes.add("string(function(){ var t = [1,2,3,4]; arrayIter(t, function(k, v){ v=k; }); return t; }())"); - values.add("[1, 2, 3, 4]"); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void arraySort() throws Exception { - - // On lance le test - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - // codes.add("string(arraySort([0,1,2,3], function(e, f){return - // e>f;}))"); - codes.add("string(function(){var t = [0,1,2]; return arraySort(t,function(e, f){return (e>f)?(-1):(ek2)?(-1):(k1k2)?(-1):(k1 mCodes, List mValues) throws Exception { - String leekscript = "return ["; - AbstractLeekValue[] values = new AbstractLeekValue[mValues.size()]; - - for (int i = 0; i < mValues.size(); i++) { - if (i != 0) - leekscript += ","; - leekscript += mCodes.get(i); - Object c = mValues.get(i); - if (c instanceof Integer) - values[i] = new IntLeekValue(((Integer) mValues.get(i))); - else if (c instanceof Double) - values[i] = new DoubleLeekValue(((Double) mValues.get(i))); - else if (c instanceof String) - values[i] = new StringLeekValue(((String) mValues.get(i))); - else if (c instanceof Boolean) - values[i] = new BooleanLeekValue(((Boolean) mValues.get(i))); - else if (c instanceof AbstractLeekValue) - values[i] = (AbstractLeekValue) mValues.get(i); - else - values[i] = new NullLeekValue(); - } - - leekscript += "];"; - try { - return LeekScript.testScript(leekscript, new ArrayLeekValue(ai, values)); - } catch (LSException e) { - int i = e.getIndex(); - System.err.println("Erreur :\n" + mCodes.get(i)); - System.err.println("Valeur attendue :\n" + e.getThe().getString(ai)); - System.err.println("Valeur renvoyée :\n" + e.getRun().getString(ai)); - return false; - } - } -} diff --git a/src/test/java/test/TestArray.java b/src/test/java/test/TestArray.java new file mode 100644 index 00000000..c8559a8b --- /dev/null +++ b/src/test/java/test/TestArray.java @@ -0,0 +1,524 @@ +package test; + +public class TestArray extends TestCommon { + + public void run() throws Exception { + + section("Array.constructor()"); + code_v3_("return Array").equals(""); + code_v3_("return Array()").equals("[]"); + code_v3_("return new Array").equals("[]"); + code_v3_("return new Array()").equals("[]"); + code_v3_("return new Array() + 1").equals("[1]"); + code("return [];").equals("[]"); + code("return [1];").equals("[1]"); + code("return [1, 2, 3];").equals("[1, 2, 3]"); + // DISABLED_code("[1l, 2l, 3l]").equals("[1, 2, 3]"); + code_v1("return [1.21, -5, 4.55, 12, -6.7];").equals("[1,21, -5, 4,55, 12, -6,7]"); + code_v2_("return [1.21, -5, 4.55, 12, -6.7];").equals("[1.21, -5, 4.55, 12, -6.7]"); + code("return [true, false, true];").equals("[true, false, true]"); + code_v2_("return [23, true, 'salut', {}, 123]").equals("[23, true, salut, {}, 123]"); + // DISABLED_code("var a = x -> x [1, 2, a]").equals("[1, 2, ]"); + // DISABLED_code("[1m, 34324234m, 231232131232132134379897874534243257343341432423m]").equals("[1, 34324234, 231232131232132134379897874534243257343341432423]"); + // DISABLED_code("[true, 'hello', 231232131232132134379897874534243257343341432423m]").equals("[true, 'hello', 231232131232132134379897874534243257343341432423]"); + + section("Array & variables"); + code("var a = []; return a;").equals("[]"); + code_v1("var a = @[]; return a;").equals("[]"); + code_v1("var a = [1, 2, 3] var b = a push(b, 4) return a;").equals("[1, 2, 3]"); + code_v1("var a = [1, 2, 3] var b = (function() { return @a })() push(b, 4) return a;").equals("[1, 2, 3, 4]"); + + section("Array.operator +"); + code("return [1, 2, 3] + [4, 5, 6];").equals("[1, 2, 3, 4, 5, 6]"); + code("return [] + 1;").equals("[1]"); + code("return [] + 1 + 2 + 3;").equals("[1, 2, 3]"); + code("return [1] + 2;").equals("[1, 2]"); + code_v1("return [1] + 0.5;").equals("[1, 0,5]"); + code_v2_("return [1] + 0.5;").equals("[1, 0.5]"); + // code_v1("return [0.5] + 'a';").equals("[0,5, a]"); + // code_v2_("return [0.5] + 'a';").equals("[0.5, a]"); + code("return ['a'] + ['b'];").equals("[a, b]"); + // code_v1("return [1] + 0.5 + 'a';").equals("[1, 0,5, a]"); + // code_v2_("return [1] + 0.5 + 'a';").equals("[1, 0.5, a]"); + // code_v1("return [1] + 0.5 + 'a' + 'b';").equals("[1, 0,5, a, b]"); + // code_v2_("return [1] + 0.5 + 'a' + 'b';").equals("[1, 0.5, a, b]"); + code("return [1] + null;").equals("[1, null]"); + code("return [1] + true;").equals("[1, true]"); + code("return [1] + [2] + [3];").equals("[1, 2, 3]"); + code_v1("return [1] + [2.5] + ['a'];").equals("[1, 2,5, a]"); + code_v2_("return [1] + [2.5] + ['a'];").equals("[1, 2.5, a]"); + code_v1("return ['a'] + [2.5] + [1];").equals("[a, 2,5, 1]"); + code_v2_("return ['a'] + [2.5] + [1];").equals("[a, 2.5, 1]"); + code("return [1] + ['a'];").equals("[1, a]"); + code("return ['a'] + [1];").equals("[a, 1]"); + // DISABLED_code("return [12.5] + null;").equals("[12.5, null]"); + // DISABLED_code("var a = ['yo'] return a + null;").equals("['yo', null]"); + // DISABLED_code("return [12.5] + true;").equals("[12.5, true]"); + // DISABLED_code("return ['yo'] + true;").equals("['yo', true]"); + // DISABLED_code("var a = ['yo'] return a + true;").equals("['yo', true]"); + // DISABLED_code("return [15.5] + [12, ''][0];").equals("[15.5, 12]"); + // DISABLED_code("var a = [15.5] return a + [12, ''][0];").equals("[15.5, 12]"); + // DISABLED_code("var a = [1] return a + [12, ''][0];").equals("[1, 12]"); + // DISABLED_code("return ['yo'] + '!';").equals("['yo', '!']"); + // DISABLED_code("var a = ['yo'] return a + '!';").equals("['yo', '!']"); + code("var a = [5] var b = ['b'] return a + b;").equals("[5, b]"); + code("var a = ['a'] return a + ['b'];").equals("[a, b]"); + code_v2_("return [1, 2] + {}").equals("[1, 2, {}]"); + code_v2_("var a = [1, 2] return a + {};").equals("[1, 2, {}]"); + code_v2_("return ['a', 'b'] + {}").equals("[a, b, {}]"); + code_v2_("var a = ['a', 'b'] return a + {}").equals("[a, b, {}]"); + // DISABLED_code("return ['a', 'b'] + (x -> x)").equals("['a', 'b', ]"); + // DISABLED_code("var a = ['a', 'b'] a + (x -> x)").equals("['a', 'b', ]"); + // DISABLED_code("['a', 'b'] + Number").equals("['a', 'b', ]"); + // DISABLED_code("var a = ['a', 'b'] a + Number").equals("['a', 'b', ]"); + // DISABLED_code("var a = [1, 2] return a + [3, 4];").equals("[1, 2, 3, 4]"); + // DISABLED_code("var a = ['a'] return [3.5, 4.6] + a;").equals("[3.5, 4.6, 'a']"); + // DISABLED_code("var pq = [] pq = pq + 1 return pq;").equals("[1]"); + // DISABLED_code("[1l] + 12l").equals("[1, 12]"); + // DISABLED_code("[] + 12l").equals("[12]"); + + section("null array"); + code("var a = null return a[1]").equals("null"); + code("var a = null return a['a']").equals("null"); + code("var a = null return a[1] = 12").equals("null"); + + section("Array misc"); + code("return [1, 2, 3] + null").equals("[1, 2, 3, null]"); + code("var a = [1] return a % 2").equals("1"); + code_v1("var a = [[1], [1]]; return (a[0] + a[2]) / 2").equals("1"); + code_v2_("var a = [[1], [1]]; return (a[0] + a[2]) / 2").equals("1.0"); + code_v1("var effects = [[1], [1]];\n\nreturn (effects[0] + effects[2]) /2;").equals("1"); + code_v2_("var effects = [[1], [1]];\n\nreturn (effects[0] + effects[2]) /2;").equals("1.0"); + + section("Array.operator []"); + code("return [1, 2, 3][1]").equals("2"); + code("var a = [1, 2, 3] return a[0]").equals("1"); + code_v1("var a = [1.6, 2.5, 3.4] return a[0]").equals("1,6"); + code_v2_("var a = [1.6, 2.5, 3.4] return a[0]").equals("1.6"); + code("var a = [1, 2, 3] a[0] = 5 return a[0]").equals("5"); + // code("var a = [23, 23, true, '', [], 123]; return |a|").equals("6"); + code("var a = [] return !a").equals("true"); + code("var a = [1, 2, 3] a[1] = 12 return a").equals("[1, 12, 3]"); + code_v1("return [1.2, 321.42, 23.15]").equals("[1,2, 321,42, 23,15]"); + code_v2_("return [1.2, 321.42, 23.15]").equals("[1.2, 321.42, 23.15]"); + // code("return [1, 2, 3, 4, 5][1:3]").equals("[2, 3, 4]"); + code("var a = [5, 'yolo', 12] return a[1]").equals("yolo"); + code("var a = [12]; a[0]++; return a").equals("[13]"); + // code("[1, 2, 'a'][['salut', 2][0]]").exception(ls::vm::Exception::ARRAY_KEY_IS_NOT_NUMBER); + code("return ['a', 'b', 'c'][[2, ''][0]]").equals("c"); + code("var a = [[12], ''][0]; a[0]++; return a").equals("[13]"); + // code("var a = [[12], ''][0] a[a]++ a").exception(ls::vm::Exception::ARRAY_KEY_IS_NOT_NUMBER); + code_v1("var a = [[12], [5.5], ['a']] a[0][0] += 1 a[1][0] += 1 a[2][0] += 1 return a").equals("[[13], [6,5], [a1]]"); + code_v2_("var a = [[12], [5.5], ['a']] a[0][0] += 1 a[1][0] += 1 a[2][0] += 1 return a").equals("[[13], [6.5], [a1]]"); + // code("var a = [1, 2, 3] return a[0l]").equals("1"); + // code("var a = [1, 2, 3] return a[1l]").equals("2"); + // code("var a = [1, 2, 3] return a[2m]").equals("3"); + code("var a = ['a', 'b', 'c'] return a[0.5]").equals("a"); + code("var a = ['a', 'b', 'c'] return a[1.9]").equals("b"); + code("return ['', [2][0]]").equals("[, 2]"); + code_v1("return ['', [2.5][0]]").equals("[, 2,5]"); + code_v2_("return ['', [2.5][0]]").equals("[, 2.5]"); + code("var a = [1, 2, 3] return a[true]").equals("2"); + code_v1("return [1, 2.5][1]").equals("2,5"); + code_v2_("return [1, 2.5][1]").equals("2.5"); + code("return [1, true][0]").equals("1"); + code("return [1, true][1]").equals("true"); + // code("return [5l, 7l, 9l][2l]").equals("9"); + code("var a = [12: 5] return a[5] = 7").equals("7"); + code("var a = [12: 5] var b = 7 return a[5] = b").equals("7"); + code("var a = [] return a[0] + 2").equals("2"); + code("var a = 5 return a[0] + 2").equals("2"); + code("var a = [1] return a[0] = 10").equals("10"); + code("var a = null return a[0]").equals("null"); + + section("[] operator on unknown arrays"); + code("var v = [['a', 'b'], 12] return v[0][0]").equals("a"); + code("var v = [['a', 'b'], 12] return v[0][1]").equals("b"); + code("var v = [['a', 'b'], 12] return v[0][true]").equals("b"); + // code("[['a', 'b'], 12][0][['yolo', 1][0]]").exception(ls::vm::Exception::ARRAY_KEY_IS_NOT_NUMBER); + code("return [['a', 'b'], 12][0][2]").equals("null"); + code("var v = [['a', 'b'], 12] v[0][0] = 5 return v").equals("[[5, b], 12]"); + code("var v = [['a', 'b'], 12] v[0][2] = 5 return v").equals("[[a, b, 5], 12]"); + // code("var a = [[12], [1..10]][1] return a[5]").equals("6"); + + section("Out of bounds exception"); + code("return [][1]").equals("null"); + code("return [1, 2, 3][100]").equals("null"); + code("var a = [1, 2, 3] return a[10]").equals("null"); + code("return [5.6, 7.2][-5]").equals("null"); + code("return ['hello', true][2]").equals("null"); + code("var a = [1, 2, 3] return a[100] = 12").equals("12"); + code("var a = [1, 2, 3] return a[-100] = 12").equals("12"); + code("var a = [] a[100] = true return a").equals("[100 : true]"); + code("var a = [1, 2, 3] a[100] = true return a").equals("[0 : 1, 1 : 2, 2 : 3, 100 : true]"); + // code("var a = [[12], ''][0]; a[100]++; return a").equals("null"); + // code("var a = [5] var e = a[1] !? 5 return e").equals("5"); + + section("Access with booleans"); + code("return [1, 2, 3][false]").equals("1"); + code("return [1, 2, 3][true]").equals("2"); + code("return ['1', '2', '3'][false]").equals("1"); + code_v1("return [1.5, 2.5, 3.5][true]").equals("2,5"); + code_v2_("return [1.5, 2.5, 3.5][true]").equals("2.5"); + + // section("Push with empty array access"); + // code("var a = [] a[] = 12 return a").equals("[12]"); + // code("var a = [1, 2] a[] = 3 return a").equals("[1, 2, 3]"); + // code("var a = [1, 2] a[] = 3 return a").equals("[1, 2, 3]"); + // code("var a = [] a[] = 'a' return a").equals("[a]"); + // code("var a = ['a', 'b'] a[] = 'c' return a").equals("[a, b, c]"); + // code("var a = [1, 'b', true] a[] = function(x) { return x } return a").equals("[1, b, true, ]"); + // code("var a = ['a', 'b', 'c'] a[] = ['d'][0] return a").equals("[a, b, c, d]"); + + section("Methods calls on unknown array"); + code("var a = [1, [1, 2]] return count(a[1])").equals("2"); + code("var a = [1, [1, 2]] push(a[1], 3) return a[1]").equals("[1, 2, 3]"); + code("var a = [1, ['a', 'b']] push(a[1], 'c') return a[1]").equals("[a, b, c]"); + code("var a = [[], ['a']] push(a[1], 'b') return a").equals("[[], [a, b]]"); + + section("Array.operator +="); + // DISABLED_code("var a = [1.55] a += 12.9 return a;").equals("[1.55]"); + // DISABLED_code("var a = ['a'] a += 'b' return a;").equals("['a', 'b']"); + // DISABLED_code("var a = [1, 2, 3] a[0] += 5 return a[0];").equals("6"); + // DISABLED_code("var v = 12 var a = [v, 2, 3] a[0] += 5 return a[0];").equals("17"); + // DISABLED_code("var a = [1, 2, 3] a += 'hello' return a;").equals("[1, 2, 3, 'hello']"); + code_v1("var a = [1.5] a += ['a', 'b'] return a").equals("[1,5, a, b]"); + code_v2_("var a = [1.5] a += ['a', 'b'] return a").equals("[1.5, a, b]"); + // DISABLED_code("var a = [1.5] a += false return a;").equals("[1.5, false]"); + // DISABLED_code("var a = [1] a += <2, 3> a").equals("[1, 2, 3]"); + // DISABLED_code("var a = [1] a += <5.5, 7.314> a").equals("[1, 5.5, 7.314]"); + // DISABLED_code("var a = [1] a += <5, 7.314, 'hello'> a").equals("[1, 5, 7.314, 'hello']"); + // DISABLED_code("var a = [1] a += [<5, 7.314, 'hello'>, ''][0] a").equals("[1, 5, 7.314, 'hello']"); + // DISABLED_code("var a = [1] a += <'z', 'a'> a").equals("[1, 'a', 'z']"); + // DISABLED_code("var a = [1] a += 'a' return a;").equals("[1, 'a']"); + // DISABLED_code("var a = [[1]] a[0] += [12, ''][0] return a[0];").equals("[1, 12]"); + code_v1("var a = [1.11] a += [2, 3] return a").equals("[1,11, 2, 3]"); + code_v2_("var a = [1.11] a += [2, 3] return a").equals("[1.11, 2, 3]"); + // DISABLED_code("var a = [[1.55]] a[0] += [12.9, ''][0] return a[0];").equals("[1.55, 12.9]"); + // DISABLED_code("var a = [[1.55]] a[0] += [-1.5, 6.7] return a[0];").equals("[1.55, -1.5, 6.7]"); + // DISABLED_code("var a = [[1.55]] a[0] += <8, 4> a[0]").equals("[1.55, 4, 8]"); + // DISABLED_code("var a = [[1.55]] a[0] += < -8.5, 4.7> a[0]").equals("[1.55, -8.5, 4.7]"); + // DISABLED_code("var a = [[1.55]] a[0] += < -8.5, 4.7, 'hello'> a[0]").equals("[1.55, -8.5, 4.7, 'hello']"); + code("var a = ['a'] return a += [1, 2]").equals("[a, 1, 2]"); + code_v1("var a = ['a'] return a += [1.5, 2.5]").equals("[a, 1,5, 2,5]"); + code_v2_("var a = ['a'] return a += [1.5, 2.5]").equals("[a, 1.5, 2.5]"); + // DISABLED_code("var a = ['a'] a += <1, 2>").equals("['a', 1, 2]"); + // DISABLED_code("var a = ['a'] a += <1.5, 2.5>").equals("['a', 1.5, 2.5]"); + // DISABLED_code("var a = ['a'] var b = <'b'> a += b a").equals("['a', 'b']"); + code("var a = ['a'] var b = [12] a += b return a").equals("[a, 12]"); + // DISABLED_code("var a = [1, 2, 3] a[1] += 0.5 return a;").equals("[1, 2.5, 3]"); + code("var a = [1, 2, 3] a += [4] return a").equals("[1, 2, 3, 4]"); + // DISABLED_code("var a = [1, 2, 3] a[1] += 500l a").equals("[1, 502, 3]"); + // DISABLED_code("var a = [] if (true) a += 12 return a;").equals("[12]"); + // DISABLED_code("var a = [1] if (true) a += 12 return a;").equals("[1, 12]"); + // DISABLED_code("var a = ['a'] if (true) a += 12 return a;").equals("['a', 12]"); + code_v1("var a = [1.55]; a += 12.9; return a").equals("[1,55, 12,9]"); + code_v2_("var a = [1.55]; a += 12.9; return a").equals("[1.55, 12.9]"); + + section("Array.operator += on element"); + code("var a = [5] a[0] += 1 return a;").equals("[6]"); + code("var a = [5, 6, 7] a[0] += 10 a[1] += 10 return a;").equals("[15, 16, 7]"); + code("var a = [[5]] a[0][0] += 1 return a;").equals("[[6]]"); + code("var a = [] a[0] += 1 return a;").equals("[1]"); + + section("Array.operator ++ on element"); + code("var a = [5]; a[0]++; return a;").equals("[6]"); + code("var a = [5, 6, 7]; a[0]++; a[1]++; return a;").equals("[6, 7, 7]"); + code("var a = [[5]]; a[0][0]++; return a;").equals("[[6]]"); + + section("Array.operator pre ++ on element"); + code("var a = [5]; ++a[0]; return a;").equals("[6]"); + code("var a = [5]; return ++a[0];").equals("6"); + code("var a = [5, 6, 7]; ++a[0]; ++a[1]; return a;").equals("[6, 7, 7]"); + code("var a = [[5]]; ++a[0][0]; return a;").equals("[[6]]"); + code("var a = [[5]]; return ++a[0][0];").equals("6"); + + section("Array.operator -- on element"); + code("var a = [5] a[0]-- return a;").equals("[4]"); + code("var a = [5, 6, 7] a[0]-- a[1]-- return a;").equals("[4, 5, 7]"); + code("var a = [[5]] a[0][0]-- return a;").equals("[[4]]"); + + section("Array.operator pre -- on element"); + code("var a = [5]; --a[0] return a;").equals("[4]"); + code("var a = [5] return --a[0];").equals("4"); + code("var a = [5, 6, 7]; --a[0]; --a[1] return a;").equals("[4, 5, 7]"); + code("var a = [[5]]; --a[0][0] return a;").equals("[[4]]"); + code("var a = [[5]] return --a[0][0];").equals("4"); + + section("Array.operator -= on element"); + code("var a = [5] a[0] -= 1 return a;").equals("[4]"); + code("var a = [5, 6, 7] a[0] -= 10 a[1] -= 10 return a;").equals("[-5, -4, 7]"); + code("var a = [[5]] a[0][0] -= 1 return a;").equals("[[4]]"); + code("var a = [] a[0] -= 1 return a;").equals("[-1]"); + + section("Array.operator *= on element"); + code("var a = [5] a[0] *= 10 return a;").equals("[50]"); + code("var a = [5, 6, 7] a[0] *= 10 a[1] *= 10 return a;").equals("[50, 60, 7]"); + code("var a = [[5]] a[0][0] *= 10 return a;").equals("[[50]]"); + + section("Array.operator %= on element"); + code("var a = [5] a[0] %= 2 return a;").equals("[1]"); + code("var a = [5, 6, 7] a[0] %= 2 a[1] %= 2 return a;").equals("[1, 0, 7]"); + code("var a = [[5]] a[0][0] %= 2 return a;").equals("[[1]]"); + + section("Array.operator **= on element"); + code("var a = [5] a[0] **= 2 return a;").equals("[25]"); + code("var a = [5, 6, 7] a[0] **= 2 a[1] **= 2 return a;").equals("[25, 36, 7]"); + code("var a = [[5]] a[0][0] **= 2 return a;").equals("[[25]]"); + + section("Array.operator |= on element"); + code("var a = [5] a[0] |= 2 return a;").equals("[7]"); + code("var a = [5, 6, 7] a[0] |= 8 a[1] |= 8 return a;").equals("[13, 14, 7]"); + code("var a = [[5]] a[0][0] |= 2 return a;").equals("[[7]]"); + code("var a = [] a[0] |= 1 return a;").equals("[1]"); + + section("Array.operator &= on element"); + code("var a = [87619] a[0] &= 18431 return a;").equals("[17987]"); + + section("Array.operator ^= on element"); + code_v1("var a = [876] a[0] ^= 3 return a;").equals("[672221376]"); + code_v2_("var a = [876] a[0] ^= 3 return a;").equals("[879]"); + + section("Array.operator <<= on element"); + code("var a = [123] a[0] <<= 12 return a;").equals("[503808]"); + + section("Array.operator >>= on element"); + code("var a = [123123123] a[0] >>= 5 return a;").equals("[3847597]"); + + section("Array.operator >>>= on element"); + code("var a = [-155] a[0] >>>= 5 return a;").equals("[134217723]"); + + section("Array.count()"); + code("return count([1, 2, 3, 4, 5]);").equals("5"); + + section("Array.push()"); + code("var a = [] push(a, 12) return a;").equals("[12]"); + code("var a = [] push(a, 1) push(a, 2) push(a, 3) return a;").equals("[1, 2, 3]"); + code("var a = [1, 2, 3] push(a, 4) return a;").equals("[1, 2, 3, 4]"); + code("var a = [1, 2, 3] push(a, 'hello') return a;").equals("[1, 2, 3, hello]"); + code("return push([], 'hello');").equals("null"); + code("var a = [[]] push(a[0], 'hello') return a;").equals("[[hello]]"); + code("var a = [[]] push(a, [1, 2, 3]) return a;").equals("[[], [1, 2, 3]]"); + code("var a = [1, [1, 2]] push(a[1], 3) return a;").equals("[1, [1, 2, 3]]"); + code("var a = [1, [1, 2]] push(a[1], 3) return a[1];").equals("[1, 2, 3]"); + code("var a = [1, ['a', 'b']] push(a[1], 'c') return a[1];").equals("[a, b, c]"); + code("var a = [[], ['a']] push(a[1], 'b') return a;").equals("[[], [a, b]]"); + code("return function() { var a = []; for (var i = 0; i < 100000; ++i) { push(a, i); } return a[91212]; }();").equals("91212"); + code("return function() { var a = ['a','b','c','d']; push(a, 'b'); return string(a); }();").equals("[a, b, c, d, b]"); + + section("Array.pushAll()"); + code("var a = [] pushAll(a, [1, 2, 3]) return a;").equals("[1, 2, 3]"); + code("var a = [1, 2, 3] pushAll(a, [4, 5, 6]) return a;").equals("[1, 2, 3, 4, 5, 6]"); + code("var a = [] var b = [1, 2, 3] pushAll(a, b) return a;").equals("[1, 2, 3]"); + code("var a = [1, 2, 3] var b = [4, 5, 6] pushAll(a, b) return a;").equals("[1, 2, 3, 4, 5, 6]"); + code("var a = [[], ['a']] pushAll(a[1], ['b', 'c']) return a;").equals("[[], [a, b, c]]"); + code("return function() { var a = [1, 2, 3]; pushAll(a, [5, 6, 7]); return a; }();").equals("[1, 2, 3, 5, 6, 7]"); + + section("Array.filter() v1.0"); + code_v1("return arrayFilter([1, 2, 3, 4, 5, 6, 7, 8, 9], function(e) { return e > 5; });").equals("[5 : 6, 6 : 7, 7 : 8, 8 : 9]"); + code_v1("return arrayFilter([4, 5, 6, 'test', 8, 9], function(e) { return e == 'test'; });").equals("[3 : test]"); + code_v1("return arrayFilter(['a', 'b', 'c', 'd'], function(k, v) { return k == 3; });").equals("[3 : d]"); + code_v1("return function() { var t = ['a', 'b', 'c', 'd']; arrayFilter(t, function(k, @v) { v = 4; return k == 3; }); return t; }();").equals("[4, 4, 4, 4]"); + code_v1("return arrayFilter(['a', 'b', 'c', 'd'], function(k, @v) { v = 4; return k == 3; });").equals("[3 : 4]"); + + section("Array.filter() v1.1"); + code_v2_("return arrayFilter([1, 2, 3, 4, 5, 6, 7, 8, 9], function(e) { return e > 5; });").equals("[6, 7, 8, 9]"); + code_v2_("return arrayFilter([4, 5, 6, 'test', 8, 9], function(e) { return e == 'test'; });").equals("[test]"); + code_v2_("return string(arrayFilter(['a', 'b', 'c', 'd'], function(k, v) { return k == 3; }));").equals("[d]"); + + section("Array.flatten()"); + code("return arrayFlatten([6,7,[8,9]],99);").equals("[6, 7, 8, 9]"); + code("return arrayFlatten([6,[[7]],[8,9]],2);").equals("[6, 7, 8, 9]"); + code("return arrayFlatten([6,[[7]],[8,9]]);").equals("[6, [7], 8, 9]"); + + section("Array.sort"); + code_v1("return function() { var t = [null, null, 4, 8, 9]; sort(t); return t; }();").equals("[4, 8, 9, null, null]"); + code_v2_("return function() { var t = [null, null, 4, 8, 9]; sort(t); return t; }();").equals("[null, null, 4, 8, 9]"); + code_v1("return function() { var t = [4, null, 4, null, 4]; sort(t); return t; }();").equals("[4, 4, 4, null, null]"); + code_v2_("return function() { var t = [4, null, 4, null, 4]; sort(t); return t; }();").equals("[null, null, 4, 4, 4]"); + code_v1("return function() { var t = [4, null, 5, null, 8]; sort(t, SORT_DESC); return t; }();").equals("[null, null, 8, 5, 4]"); + code_v2_("return function() { var t = [4, null, 5, null, 8]; sort(t, SORT_DESC); return t; }();").equals("[8, 5, 4, null, null]"); + + section("Array and references"); + code_v1("var t = [@3, @4, @5]; return t;").equals("[3, 4, 5]"); + code_v1("var t = [3, 4, 5]; var a = 12; t[1] = @a return t;").equals("[3, 12, 5]"); + code_v1("var t = [3, 4, 5]; var a = @t[1] a++ return t;").equals("[3, 4, 5]"); + code_v1("var t = [3, 4, 5]; var a = null a = @t[1] a++ return t;").equals("[3, 4, 5]"); + code_v1("var t = [3, 4, 5]; t[3] = [1, 2, 3, 4]; var r = @t[3]; r[4] = 'coucou'; return t;").equals("[3, 4, 5, [1, 2, 3, 4, coucou]]"); + + section("Array.map()"); + code("return arrayMap([1, 2, 3], function(v) { var r = [] return r })").equals("[[], [], []]"); + code("return arrayMap([1, 2, 3], function(v) { var r = [1, 2, 3] return r })").equals("[[1, 2, 3], [1, 2, 3], [1, 2, 3]]"); + code("var r = [] var a = arrayMap([1, 2, 3], function(v) { return r }) push(r, 1) return a").equals("[[1], <...>, <...>]"); + code_v2_("class A { name part constructor(name, part) { this.name = name this.part = part } } var list = [new A('foo', true), new A('bar', false), new A('baz', true)] return arrayMap(list, function(a) { return a.name })").equals("[foo, bar, baz]"); + + section("Array.map() v1.0"); + code_v1("return arrayMap([1, 2, 3, 4, 5], function(e) { return e * 2; });").equals("[2, 4, 6, 8, 10]"); + code_v1("return arrayMap([4, 9, 16], sqrt);").equals("[2, 3, 4]"); + code_v1("return arrayMap(['a': 1,'b': 2], function(k, v) { return k + v; });").equals("[a : a1, b : b2]"); + code_v1("return function() { var t = ['a':1,'b':2]; arrayMap(t, function(@k, @v) { v = 'tomate'; k = 'ctus'; return 3; }); return t; }();").equals("[a : tomate, b : tomate]"); + + section("Array.map() v1.1"); + code_v2_("return arrayMap([1, 2, 3, 4, 5], function(e) { return e * 2; });").equals("[2, 4, 6, 8, 10]"); + code_v2_("return arrayMap([4, 9, 16], sqrt);").equals("[2.0, 3.0, 4.0]"); + code_v2_("return arrayMap(['a': 1, 'b': 2], function(k, v) { return k + v; });").equals("[a : a1, b : b2]"); + code_v2_("return function() { var t = ['a': 1, 'b': 2]; arrayMap(t, function(k, v) { v = 'tomate'; k = 'ctus'; return 3; }); return t; }();").equals("[a : 1, b : 2]"); + + section("Array.foldLeft()"); + code("return arrayFoldLeft([6, 7, 8, 9], function(a, b) { return a + b; }, 0)").equals("30"); + code("return arrayFoldLeft([1,0,1,2,5,7,9], function(a,b){return a+','+b;},'')").equals(",1,0,1,2,5,7,9"); + + section("Array.foldRight()"); + code("return arrayFoldRight([6,7,8,9], function(a,b){return a+b;},0)").equals("30"); + code("return arrayFoldRight([1,0,1,2,5,7,9], function(a,b){return a+','+b;},'')").equals("1,0,1,2,5,7,9,"); + + section("Array.partition()"); + code("return arrayPartition([6,7,8,9], function(a){return a&1;})").equals("[[1 : 7, 3 : 9], [0 : 6, 2 : 8]]"); + code("return string(arrayPartition([6,7,8,9], function(k,v){return k;}))").equals("[[1 : 7, 2 : 8, 3 : 9], [6]]"); + code("return string(arrayPartition([4,3,2,1], function(k,v){return kf)?(-1):(ek2)?(-1):(k1k2)?(-1):(k1 operationsReference = new ArrayList<>(); + // private static int operationsReferenceIndex = 0; + private static ArrayList operations = new ArrayList<>(); + + private static List failedTests = new ArrayList(); + private static List disabledTests = new ArrayList(); + + public static class Case { + String code; + boolean enabled = true; + int version_min = 1; + int version_max = 3; + + public Case(String code, boolean enabled) { + this.code = code; + this.enabled = enabled; + } + + public Case(String code, boolean enabled, int version_min, int version_max) { + this.code = code; + this.enabled = enabled; + this.version_min = version_min; + this.version_max = version_max; + } + + public String equals(String expected) { + return run(new Checker() { + public boolean check(Result result) { + return result.result.equals(expected); + } + public String getExpected() { return expected; } + public String getResult(Result result) { return result.result; } + }); + } + + public String error(Error type) { + return run(new Checker() { + public boolean check(Result result) { + return result.error == type; + } + public String getExpected() { return type.name(); } + public String getResult(Result result) { return result.error.name(); } + }); + } + + public String any_error() { + return run(new Checker() { + public boolean check(Result result) { + return result.error != Error.NONE; + } + public String getExpected() { return "no error"; } + public String getResult(Result result) { return result.error.name(); } + }); + } + + public void almost(double expected) { + almost(expected, 1e-10); + } + + public void almost(double expected, double delta) { + run(new Checker() { + public boolean check(Result result) { + try { + double r = Double.parseDouble(result.result); + return Math.abs(r - expected) < delta; + } catch (Exception e) { + return false; + } + } + public String getExpected() { return String.valueOf(expected); } + public String getResult(Result result) { return result.result; } + }); + } + + public String run(Checker checker) { + if (!enabled) { + disabled++; + var s = C_PINK + "[DISA] " + END_COLOR + "[v" + version_min + "-" + version_max + "] " + code; + System.out.println(s); + disabledTests.add(s); + return "disabled"; + } + for (int v = version_min; v <= version_max - 1; ++v) { + run_version(v, checker); + } + return run_version(version_max, checker); + } + public String run_version(int version, Checker checker) { + tests++; + int aiID = 0; + Result result; + long compile_time = 0; + long ops = 0; + try { + boolean is_file = code.contains(".leek"); + + AI ai = is_file ? LeekScript.compileFile(code, "AI", version) : LeekScript.compileSnippet(code, "AI", version); + ai.staticInit(); + aiID = ai.getId(); + + compile_time = ai.getCompileTime() / 1000000; + TestCommon.analyze_time += ai.getAnalyzeTime() / 1000000; + TestCommon.compile_time += ai.getCompileTime() / 1000000; + // TestCommon.load_time += ai.getLoadTime() / 1000000; + + ai.maxOperations = Integer.MAX_VALUE; + + long t = System.nanoTime(); + var v = ai.runIA(); + long exec_time = (System.nanoTime() - t) / 1000; + TestCommon.execution_time += exec_time / 1000; + + ops = ai.operations(); + + var vs = ai.getString(v, new HashSet<>()); + result = new Result(vs, Error.NONE, (int) ai.getOperations(), exec_time); + + } catch (LeekCompilerException e) { + e.printStackTrace(); + // System.out.println("Error = " + e.getError()); + result = new Result(e.getError().toString(), e.getError(), 0, 0); + } catch (Exception e) { + e.printStackTrace(); + result = new Result("error", Error.NONE, 0, 0); + } + + operations.add(ops); + // long referenceOperations = operationsReference.get(operationsReferenceIndex++); + + if (checker.check(result)) { + System.out.println(GREEN_BOLD + " [OK] " + END_COLOR + "[v" + version + "] " + code + " === " + checker.getResult(result) + " " + C_GREY + compile_time + "ms + " + fn(result.exec_time) + "µs" + ", " + fn(result.operations) + " ops" + END_COLOR); + success++; + } else { + var err = C_RED + "[FAIL] " + END_COLOR + "[v" + version + "] " + code + " =/= " + checker.getExpected() + " got " + checker.getResult(result) + "\n" + + "/home/pierre/dev/leek-wars/server/daemon/generator-v1/leekscript-v1/ai/AI_" + aiID + ".java"; + System.out.println(err); + failedTests.add(err); + } + return result.result; + } + } + + public static class Result { + String result; + Error error; + int operations; + long exec_time; + + public Result(String result, Error error, int operations, long exec_time) { + this.result = result; + this.operations = operations; + this.exec_time = exec_time; + this.error = error; + } + } + + public static interface Checker { + public boolean check(Result result); + + public String getResult(Result result); + + public String getExpected(); + } + + public Case code(String code) { + return new Case(code, true); + } + public Case file(String code) { + return new Case(code, true); + } + public Case file_v1(String code) { + return new Case(code, true, 1, 1); + } + public Case file_v2_(String code) { + return new Case(code, true, 2, LATEST_VERSION); + } + public Case DISABLED_file(String code) { + return new Case(code, false); + } + public Case DISABLED_file_v2_(String code) { + return new Case(code, false, 2, LATEST_VERSION); + } + public Case code_v1(String code) { + return new Case(code, true, 1, 1); + } + public Case code_v1_2(String code) { + return new Case(code, true, 1, 2); + } + public Case code_v2(String code) { + return new Case(code, true, 2, 2); + } + public Case code_v2_(String code) { + return new Case(code, true, 2, LATEST_VERSION); + } + public Case code_v3_(String code) { + return new Case(code, true, 3, LATEST_VERSION); + } + public Case DISABLED_code(String code) { + return new Case(code, false); + } + public Case DISABLED_code_v2_(String code) { + return new Case(code, false, 2, LATEST_VERSION); + } + + public void section(String title) { + System.out.println("========== " + title + " =========="); + } + public void header(String title) { + System.out.println("================================================"); + System.out.println("========== " + title + " =========="); + System.out.println("================================================"); + } + + public static boolean summary() { + System.out.println("================================================"); + System.out.println(success + " / " + tests + " tests passed, " + (tests - success) + " errors, " + disabled + " disabled"); + System.out.println("Total time: " + fn(analyze_time + compile_time + execution_time) + " ms" + + " = Analyze: " + fn(analyze_time) + " ms" + + " + Compile: " + fn(compile_time) + " ms" + + " + Execution: " + fn(execution_time) + " ms"); + System.out.println("================================================"); + + for (String test : disabledTests) { + System.out.println(test); + } + for (String test : failedTests) { + System.out.println(test); + } + return success == tests; + } + + public static String fn(long n) { + DecimalFormat formatter = (DecimalFormat) NumberFormat.getInstance(Locale.US); + DecimalFormatSymbols symbols = formatter.getDecimalFormatSymbols(); + + symbols.setGroupingSeparator(' '); + formatter.setDecimalFormatSymbols(symbols); + return formatter.format(n); + } + public static void ouputOperationsFile() { + try { + FileWriter myWriter = new FileWriter("opérations.txt"); + for (Long ops : operations) { + myWriter.write(String.valueOf(ops) + "\n"); + } + myWriter.close(); + } catch (IOException e) { + System.out.println("An error occurred."); + e.printStackTrace(); + } + } + public static void loadReferenceOperations() { + BufferedReader reader; + try { + reader = new BufferedReader(new FileReader("opérations_v1.txt")); + String line = reader.readLine(); + while (line != null) { + operationsReference.add(Long.parseLong(line)); + line = reader.readLine(); + } + System.out.println(operationsReference.size() + " test operations references loaded."); + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/test/java/test/TestEuler.java b/src/test/java/test/TestEuler.java new file mode 100644 index 00000000..2ce92172 --- /dev/null +++ b/src/test/java/test/TestEuler.java @@ -0,0 +1,42 @@ +package test; + +public class TestEuler extends TestCommon { + + public void run() { + + section("Project Euler"); + file("ai/euler/pe001.leek").equals("233168"); + file("ai/euler/pe002.leek").equals("4613732"); + file("ai/euler/pe003.leek").equals("6857"); + // DISABLED_file("ai/euler/pe004.leek").equals("906609"); + file("ai/euler/pe005.leek").equals("232792560"); + file("ai/euler/pe006.leek").equals("25164150"); + file("ai/euler/pe007.leek").equals("104743"); + // DISABLED_file("ai/euler/pe008.leek").equals("23514624000"); + file("ai/euler/pe009.leek").equals("31875000"); + // DISABLED_file("ai/euler/pe010.leek").equals("142913828922"); + // DISABLED_file("ai/euler/pe011.leek").equals("70600674"); + file("ai/euler/pe012.leek").equals("76576500"); + // DISABLED_file("ai/euler/pe013.leek").equals("5537376230"); + // Too long + DISABLED_file("ai/euler/pe014.leek").equals("837799"); + // DISABLED_file("ai/euler/pe015.leek").equals("137846528820"); + // DISABLED_file("ai/euler/pe016.leek").equals("1366"); + file("ai/euler/pe017.leek").equals("21124"); + file("ai/euler/pe018.leek").equals("1074"); + file("ai/euler/pe019.leek").equals("171"); + // DISABLED_file("ai/euler/pe020.leek").equals("648"); + // file("ai/euler/pe021.leek").equals("31626"); + // file("ai/euler/pe022.leek").equals("871198282"); + // file("ai/euler/pe023.leek").equals("4179871"); + // file("ai/euler/pe024.leek").equals("2783915460"); + // file("ai/euler/pe025.leek").equals("4782"); + // file("ai/euler/pe026.leek").equals(""); + // file("ai/euler/pe027.leek").equals("-59231"); + // file("ai/euler/pe028.leek").equals("669171001"); + // file("ai/euler/pe062.leek").equals("127035954683"); + // file("ai/euler/pe063.leek").equals("49"); + // file("ai/euler/pe064.leek").equals("1322"); + // DISABLED_file("ai/euler/pe206.leek").equals("1389019170"); + } +} diff --git a/src/test/java/test/TestFiles.java b/src/test/java/test/TestFiles.java new file mode 100644 index 00000000..0e306162 --- /dev/null +++ b/src/test/java/test/TestFiles.java @@ -0,0 +1,60 @@ +package test; + +public class TestFiles extends TestCommon { + + public void run() { + + /** Complex codes */ + header("Files"); + section("General"); + file_v2_("ai/code/primes.leek").equals("78498"); + // DISABLED_file("test/code/primes_gmp.leek").equals("9591"); + file("ai/code/gcd.leek").equals("151"); + file("ai/code/strings.leek").equals("true"); + file("ai/code/reachable_cells.leek").equals("383"); + file("ai/code/reachable_cells.leek").equals("383"); + file("ai/code/reachable_cells_variant_1.leek").equals("400"); + file("ai/code/reachable_cells_variant_2.leek").equals("481"); + file("ai/code/reachable_cells_variant_3.leek").equals("16"); + file("ai/code/reachable_cells_variant_4.leek").equals("2"); + file("ai/code/reachable_cells_variant_5.leek").equals("5"); + file("ai/code/reachable_cells_variant_6.leek").equals("4"); + file("ai/code/reachable_cells_variant_7.leek").equals("5"); + file("ai/code/reachable_cells_variant_8.leek").equals("null"); + file("ai/code/reachable_cells_variant_9.leek").equals("null"); + file("ai/code/reachable_cells_variant_10.leek").equals("null"); + file("ai/code/french.leek").equals("cent-soixante-huit millions quatre-cent-quatre-vingt-neuf-mille-neuf-cent-quatre-vingt-dix-neuf"); + // DISABLED_file("test/code/break_and_continue.leek").equals("2504"); + file("ai/code/french.min.leek").equals("neuf-cent-quatre-vingt-sept milliards six-cent-cinquante-quatre millions trois-cent-vingt-et-un-mille-douze"); + // file("test/code/quine.leek").quine(); + // file_v1("test/code/quine_zwik.leek").quine(); + // file("test/code/dynamic_operators").works(); + // file("ai/code/euler1.leek").equals("2333316668"); + // DISABLED_file("ai/code/text_analysis.leek").equals("[3, 47, 338]"); + // DISABLED_file("ai/code/divisors.leek").equals("[1, 3, 9, 13, 17, 39]"); + file_v2_("ai/code/two_functions.leek").equals("[{p: 2, v: 5}, [{p: 3, v: 6}]]"); + // file("test/code/product_n.leek").equals("5040"); + // file("test/code/product_n_return.leek").equals("265252859812191058636308480000000"); + // file("test/code/product_n_arrays.leek").equals("[5040]"); + // file("test/code/product_coproduct.leek").equals("171122452428141311372468338881272839092270544893520369393648040923257279754140647424000000000000000"); + file_v2_("ai/code/fold_left.leek").equals("[{w: 1}, {w: 3}, {w: 4}, {w: 2}, {w: 7}, {w: 5}, {w: 8}, {w: 9}, {w: 6}]"); + // file("test/code/fold_left_2.leek").equals("{p: 6, v: {p: 9, v: {p: 8, v: {p: 5, v: {p: 7, v: { ... }}}}}}"); + // file("test/code/fold_right.leek").equals("[{w: 6}, {w: 9}, {w: 8}, {w: 5}, {w: 7}, {w: 2}, {w: 4}, {w: 3}, {w: 1}]"); + // file("test/code/fold_right_2.leek").equals("{p: {p: {p: {p: {p: { ... }, v: 7}, v: 2}, v: 4}, v: 3}, v: 1}"); + file("ai/code/assignments.leek").equals("15"); + file("ai/code/recursive_2_vars.leek").equals("1021"); + file("ai/code/global_functions_1.leek").equals("false"); + file("ai/code/global_functions_2.leek").equals("[false, true]"); + file("ai/code/recursive_2_functions.leek").equals("10"); + // DISABLED_file("test/code/recursive_2_versions.leek").equals("9.5"); + // DISABLED_file("test/code/swap.leek").equals("[{p: 1}, {p: 3}, {p: 4}, {p: 12}, {p: 5}]"); + file_v2_("ai/code/classes_simple.leek").equals("[Ferrari, Maserati, Lamborghini]"); + file_v2_("ai/code/classes_multiple.leek").equals("[4, 40, 80]"); + // DISABLED_file("test/code/match.leek").output("Yeah!\n"); + file("ai/code/fibonacci.leek").equals("832040"); + // file("ai/code/fibonacci_long.leek").equals("1346269"); + // file("ai/code/pow5.leek").equals("6938893903907228377647697925567626953125"); + file("ai/code/tarai.leek").equals("16"); + file("ai/code/return_in_function.leek").equals("2"); + } +} diff --git a/src/test/java/test/TestFunction.java b/src/test/java/test/TestFunction.java new file mode 100644 index 00000000..2d3aca16 --- /dev/null +++ b/src/test/java/test/TestFunction.java @@ -0,0 +1,170 @@ +package test; + +import leekscript.common.Error; + +public class TestFunction extends TestCommon { + + public void run() { + + section("Function"); + code_v1_2("return function() {}").equals("#Anonymous Function"); + code_v1_2("return Function() {}").equals("#Anonymous Function"); + code_v1_2("return FUNCTION() {}").equals("#Anonymous Function"); + code_v3_("return function() {}").equals("#Anonymous Function"); + code_v3_("return Function() {}").error(Error.CANT_ADD_INSTRUCTION_AFTER_BREAK); + code_v3_("return FUNCTION() {}").error(Error.CANT_ADD_INSTRUCTION_AFTER_BREAK); + + section("Function toBoolean"); + code("var a = function() {} return !!a").equals("true"); + code("var a = function() {} if (a) { return 12 } return null").equals("12"); + + section("Recursive"); + code("var fact = function(x) { if (x == 1) { return 1 } else { return fact(x - 1) * x } } return fact(8);").equals("40320"); + // code("var fact = function(x) { if (x == 1) { return 1m } else { return fact(x - 1) * x } } return fact(30m);").equals("265252859812191058636308480000000"); + // code("var fact = function(x) { if (x == 1) { 1m } else { fact(x - 1) * x } } return fact(30);").equals("265252859812191058636308480000000"); + code("var fact = function(x) { if (x > 1) { return fact(x - 1) * x } else { return 1 } } return fact(10);").equals("3628800"); + code("var fib = function(n) { if (n <= 1) { return n } else { return fib(n - 1) + fib(n - 2) } } return fib(25);").equals("75025"); + // code("var fact = x -> if x > 1 x * fact(x - 1) else x fact(5)").equals("120"); + // code("var test = x -> if x > 0 { test(x - 1) } else { 77 } test(4)").equals("77"); + // code("var fact = (x, a) -> { if x == 0 then return a end return fact(x - 1, x * a) } fact(10, 1)").equals("3628800"); + // code("var fact = (x, a) -> { if x == 0m then return a end return fact(x - 1, x * a) } fact(10m, 1m)").equals("3628800"); + // code("function test() { var fact = x -> if x == 1 { 1 } else { fact(x - 1) * x } fact(8) } test()").equals("40320"); + file_v1("ai/code/knapsack.leek").equals("761"); + file_v2_("ai/code/knapsack_2.leek").equals("761"); + code("function cellsInRange(i) { var areaInRange = []; if (i == 0) { return cellsInRange(10); } else { return areaInRange; } } var myRange = cellsInRange(0); return myRange").equals("[]"); + + section("Redefinition"); + code("var count = count([1, 2, 3]) return count;").equals("3"); + code("var d = debug d('salut')").equals("null"); + code("abs = 2 return abs").equals("2"); + code("arrayFoldRight = 'salut' return arrayFoldRight").equals("salut"); + + section("System function as argument"); + code_v1("function t(@f) { return function(@a) { return arrayMap(a, f); } } return t(sqrt)([1, 4, 9, 16, 25]);").equals("[1, 2, 3, 4, 5]"); + code_v2_("function t(f) { return function(a) { return arrayMap(a, f); } } return t(sqrt)([1, 4, 9, 16, 25]);").equals("[1.0, 2.0, 3.0, 4.0, 5.0]"); + + section("Single null argument"); + code("function f(a) { return 12; } return f(null);").equals("12"); + code("var fa = [function(a) { return 12; }] return fa[0](null);").equals("12"); + + section("Capture argument"); + code_v1("function f(@a) { return function() { a += 2 } }; var x = 10 f(x)() return x;").equals("12"); + code_v2_("function f(a) { return function() { a += 2 } }; var x = 10 f(x)() return x;").equals("10"); + code_v1("var f = function(@a) { return function() { a += 2 } }; var x = 10 f(x)() return x;").equals("12"); + code_v2_("var f = function(a) { return function() { a += 2 } }; var x = 10 f(x)() return x;").equals("10"); + + section("Capture loop variable"); + code("var sum = 0 for (var i = 0; i < 10; ++i) { sum += (function() { return i })() } return sum").equals("45"); + + section("Return reference"); + code_v1("global x = 10 function f() { return @x } var a = f() a += 5 return x").equals("10"); + code("global x = 10 function f() { return x } var a = f() a += 5 return x").equals("10"); + code_v1("var x = 10 var f = function() { return @x } var a = f() a += 5 return x").equals("10"); + code("var x = 10 var f = function() { return x } var a = f() a += 5 return x").equals("10"); + code_v1("var x = [] var f = function() { return @x } var a = f() push(a, 5) return x").equals("[5]"); + code_v1("var x = [] var f = function() { return x } var a = f() push(a, 5) return x").equals("[]"); + code_v2_("var x = [] var f = function() { return x } var a = f() push(a, 5) return x").equals("[5]"); + + section("Misc"); + code("function f(x) { var s = 0 s |= 12 return s } f(12);").equals("null"); + code("function te(a){ return function(){ return a**2; }; } return te(2)();").equals("4"); + code("function te(a){ return function(b){ return function(c){return a*b*c;}; }; } return te(2)(1)(2);").equals("4"); + code("var tab = [2, 3, 4, 5, 6]; var r = []; for (var i : var j in tab) { r[i] = function() { return j; }; } return 4;").equals("4"); + code_v1("var retour = [];for(var i=0;i<5;i++){if(i&1){var sqrt=function(e){return 1;}; push(retour, sqrt(4));}else{push(retour, sqrt(4));}}return string(retour);").equals("[2, 1, 2, 1, 2]"); + code_v2_("var retour = [];for(var i=0;i<5;i++){if(i&1){var sqrt=function(e){return 1;}; push(retour, sqrt(4));}else{push(retour, sqrt(4));}}return string(retour);").equals("[2.0, 1, 2.0, 1, 2.0]"); + code_v1("var r = [1, 2, 3] var f = function() { return r } var x = f() push(x, 12) return r").equals("[1, 2, 3]"); + code_v2_("var r = [1, 2, 3] var f = function() { return r } var x = f() push(x, 12) return r").equals("[1, 2, 3, 12]"); + code("function f() { return [1, 2, 3] } var x = f();").equals("null"); + code("var x = arrayMap([1, 2, 3], function(x) { return x });").equals("null"); + code("var x = arrayMap([1, 2, 3], function(x) { return x }); debug(x);").equals("null"); + code("var toto = 12; var f = function() { toto = 'salut'; }; [true, 12, f][2](); return toto").equals("salut"); + code("var toto = 12; var f = function() { toto = 'salut'; }; var g = function() { return f; }; g()() return toto").equals("salut"); + code_v1("function Coordonate(@par_x, @par_y) { var x = par_x; var y = par_y; var getX = function(){ return x; }; var getY = function(){ return y; };return @(function(@method) { if(method === 'getX'){ return getX; } if(method === 'getY'){ return getY; } }); } var c = Coordonate(5, 12) return [c('getX')(), c('getY')()]").equals("[5, 12]"); + code_v2_("function Coordonate(par_x, par_y) { var x = par_x; var y = par_y; var getX = function(){ return x; }; var getY = function(){ return y; };return (function(method) { if(method === 'getX'){ return getX; } if(method === 'getY'){ return getY; } }); } var c = Coordonate(5, 12) return [c('getX')(), c('getY')()]").equals("[5, 12]"); + code("function test() { var r = [1, 2, 3] return (r); } return test()").equals("[1, 2, 3]"); + code("function test() { var r = [1, 2, 3] return (r); } var a = test() return a").equals("[1, 2, 3]"); + code("function t(a) {} t([ [12], [12] ])").equals("null"); + code_v1("function t(@a) {} t([ [12], [12] ])").equals("null"); + code_v1("function t() { var a = 12 return @a } return t() + 2").equals("14"); + code("function t() { var a = [1, 2, 3] return a } var x = t() var f = function() { return x } return x").equals("[1, 2, 3]"); + code("push = 1 return push").equals("1"); + code_v1("function LamaSwag() {} @LamaSwag();").equals("null"); + code("function f() { distance = 12 } function distance() { return 'salut' } return distance()").equals("salut"); + code("getOperations()").equals("null"); + code("var a = [function() { return 12 }] return a[0]()").equals("12"); + code_v1("function push_to_array(array) { return function(element) { push(array, element); } } var arrayCurry = []; var functionToCall = push_to_array(arrayCurry); for (var i = 0; i < 5; i++) functionToCall(i); return arrayCurry").equals("[]"); + code_v2_("function push_to_array(array) { return function(element) { push(array, element); } } var arrayCurry = []; var functionToCall = push_to_array(arrayCurry); for (var i = 0; i < 5; i++) functionToCall(i); return arrayCurry").equals("[0, 1, 2, 3, 4]"); + + section("Modify argument"); + code("function test(x) { x += 10 return x } return test(5)").equals("15"); + code("var a = [1, 2, 3] function test(x) { push(x, 10) return x } return [a, test([])]").equals("[[1, 2, 3], [10]]"); + + code("function f(arg, arg) { return arg } return f(1, 2)").error(Error.PARAMETER_NAME_UNAVAILABLE); + section("Knapsack variants"); + code("var items = [[37, 3], [47, 10], [28, 5]] var all = []; return count(all);").equals("0"); + code_v1("var aux; aux = function() {}; aux();").equals("null"); + code_v1("var aux = function(current) {}; aux([0, []]);").equals("null"); + code_v1("var aux = function(@current) {}; aux([0, []]);").equals("null"); + code_v1("var aux; aux = function(current, i, tp, added, last) {}; aux([0, []], 0, 25, [], -1);").equals("null"); + code_v1("var aux; aux = function(@current, i, tp, added, last) {}; aux([0, []], 0, 25, [], -1);").equals("null"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var all = []; var aux; aux = function(@current, i, tp, added, last) {}; aux([0, []], 0, 25, [], -1); return count(all);").equals("0"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var all = []; var aux; aux = function(@current, i, tp) { if (count(current[1])) push(all, current); var item_count = count(items); for (var j = i; j < item_count; ++j) { var item = @items[j]; var cost = item[1]; if (cost > tp) continue; var copy = current; push(copy[1], @[item, cost, 1]); } }; aux([0, []], 0, 25); return count(all);").equals("0"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var all = []; var aux; aux = function(@current, i, tp) { if (count(current[1])) push(all, current); var item_count = count(items); for (var j = i; j < item_count; ++j) { var item = @items[j]; var cost = item[1]; if (cost > tp) continue; var copy = current; push(copy[1], @[item, cost, 1]); aux(copy, j, tp - cost); } }; aux([0, []], 0, 25); return count(all);").equals("44"); + code_v1("var added = [] added[1] = true;").equals("null"); + code_v1("var added = [] var new_added = added;").equals("null"); + code_v1("var added = [] var new_added = added; new_added[1] = true;").equals("null"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var all = []; var aux; aux = function(@current, i, tp, added, last) { var new_added = added; new_added[1] = true; }; aux([0, []], 0, 25, [], -1); return count(all);").equals("0"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var all = []; var aux; aux = function(@current, i, tp, added, last) { if (count(current[1])) push(all, current); var item_count = count(items); for (var j = i; j < item_count; ++j) { var new_added = added; new_added[1] = true; } }; aux([0, []], 0, 25, [], -1); return count(all);").equals("0"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var all = []; var aux; aux = function(@current, i, tp, added, last) { if (count(current[1])) push(all, current); var item_count = count(items); for (var j = i; j < item_count; ++j) { var item = @items[j]; var item_id = item[0]; var new_added = added; new_added[item_id] = true; } }; aux([0, []], 0, 25, [], -1); return count(all);").equals("0"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var all = []; var aux; aux = function(@current, i, tp, added, last) { if (count(current[1])) push(all, current); var item_count = count(items); for (var j = i; j < item_count; ++j) { var item = @items[j]; var item_id = item[0]; var cost = item[1]; if (cost > tp) continue;var new_added = added; new_added[item_id] = true; } }; aux([0, []], 0, 25, [], -1); return count(all);").equals("0"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var aux; aux = function(@current, i, tp, added) { for (var j = i; j < 3; ++j) { if (tp < 0) continue; var new_added = added; new_added[2] = true; var copy = current; } }; aux([0, []], 0, 25, []);").equals("null"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var all = []; var aux; aux = function(@current, i, tp, added, last) { for (var j = i; j < 3; ++j) { if (tp < 0) continue; var new_added = added; new_added[2] = true; var copy = current; } }; aux([0, []], 0, 25, [], -1); return count(all);").equals("0"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var aux; aux = function(@current, i, tp, added) { if (tp < 0) return; var new_added = added; new_added[2] = true; }; aux([0, []], 0, 25, []);").equals("null"); + code_v1("var add = [2: 2] var copy = add;").equals("null"); + code_v1("var add = [2: true] var copy = add;").equals("null"); + code_v1("var add = [] add[2] = true; add[2] = true;").equals("null"); + code_v1("var add = [] add[2] = true; var copy = add;").equals("null"); + code_v1("var aux = function(tp, add) { if (tp < 0) return; aux(tp - 5, add); }; aux(25, []);").equals("null"); + code_v1("var aux = function(tp, add) { if (tp < 0) return; aux(tp - 5, add); }; aux(25, [1, 2, 3]);").equals("null"); + code_v1("var aux = function(tp, add) { if (tp < 0) return; push(add, 2); aux(tp - 5, add); }; aux(25, []);").equals("null"); + code_v1("var aux = function(tp, add) { if (tp < 0) return; add[2] = true; aux(tp - 5, add); }; aux(25, []);").equals("null"); + code_v1("var aux = function(tp, add) { if (tp < 0) return; var new_add = add; aux(tp - 5, new_add); }; aux(25, []);").equals("null"); + code_v1("var aux = function(tp, add) { if (tp < 0) return; var new_add = add; new_add[2] = true; aux(tp - 5, new_add); }; aux(25, []);").equals("null"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var aux; aux = function(tp, added) { if (tp < 0) return; var new_added = added; new_added[2] = true; aux(tp - 5, new_added); }; aux(25, []);").equals("null"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var aux; aux = function(@current, i, tp, added) { if (tp < 0) return; var new_added = added; new_added[2] = true; aux([], i, tp - 5, new_added); }; aux([0, []], 0, 25, []);").equals("null"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var all = []; var aux; aux = function(@current, i, tp, added) { if (tp < 0) return; var new_added = added; new_added[2] = true; aux([], i, tp - 5, new_added); }; aux([0, []], 0, 25, []); return count(all);").equals("0"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var all = []; var aux; aux = function(@current, i, tp, added) { if (tp < 0) return; var new_added = added; new_added[2] = true; var copy = current; aux(copy, i, tp - 5, new_added); }; aux([0, []], 0, 25, []); return count(all);").equals("0"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var all = []; var aux; aux = function(@current, i, tp, added, last) { for (var j = i; j < 3; ++j) { if (tp < 0) continue; var new_added = added; new_added[2] = true; var copy = current; aux(copy, j, tp - 5, new_added, 1); } }; aux([0, []], 0, 25, [], -1); return count(all);").equals("0"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var all = []; var aux; aux = function(@current, i, tp, added, last) { if (count(current[1])) push(all, current); var item_count = count(items); for (var j = i; j < item_count; ++j) { var item = @items[j]; var item_id = item[0]; var cost = item[1]; if (cost > tp) continue;var new_added = added; new_added[item_id] = true; var copy = current; aux(copy, j, tp - cost, new_added, item_id); } }; aux([0, []], 0, 25, [], -1); return count(all);").equals("0"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var all = []; var aux; aux = function(@current, i, tp, added, last) { if (count(current[1])) push(all, current); var item_count = count(items); for (var j = i; j < item_count; ++j) { var item = @items[j]; var item_id = item[0]; var cost = item[1]; if (cost > tp) continue;var new_added = added; new_added[item_id] = true; var copy = current; push(copy[1], @[item, cost, 1]); aux(copy, j, tp - cost, new_added, item_id); } }; aux([0, []], 0, 25, [], -1); return count(all);").equals("44"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var all = []; var aux; aux = function(@current, i, tp, added, last) { if (count(current[1])) push(all, current); var item_count = count(items); for (var j = i; j < item_count; ++j) { var item = @items[j]; var item_id = item[0]; var cost = item[1]; if (cost > tp) continue;var new_added = added; new_added[item_id] = true; var copy = current; push(copy[1], @[item, cost, 1]); copy[0] += cost; aux(copy, j, tp - cost, new_added, item_id); } }; aux([0, []], 0, 25, [], -1); return count(all);").equals("44"); + + section("strings.leek variations"); + code("var m = ['A', 'T', 'C', 'G'];").equals("null"); + code("var m = ['A', 'T', 'C', 'G'] var count = 0 var tests = 500 for (var k = 0; k < tests; k++) {} return abs(100 * (count / tests) - 52) < 12;").equals("false"); + code("var m = ['A', 'T', 'C', 'G'] var count = 0 var tests = 500 for (var k = 0; k < tests; k++) { var adn = '' for (var j = 0; j < 200; j++) {} } return abs(100 * (count / tests) - 52) < 12;").equals("false"); + code("var m = ['A', 'T', 'C', 'G'] var count = 0 var tests = 500 for (var k = 0; k < tests; k++) { var adn = '' for (var j = 0; j < 200; j++) {} var c = contains(adn, 'GAGA'); if (c) count++ } return abs(100 * (count / tests) - 52) < 12;").equals("false"); + code("var m = ['A', 'T', 'C', 'G'] var adn = '' adn += m[randInt(0, 4)];").equals("null"); + code("var m = ['A', 'T', 'C', 'G'] var adn = '' for (var j = 0; j < 200; j++) { adn += m[randInt(0, 4)] }").equals("null"); + code("var adn = 'testtest' contains(adn, 'GAGA');").equals("null"); + code("var m = ['A', 'T', 'C', 'G'] var adn = 'testtest' adn += m[randInt(0, 4)]").equals("null"); + code("var m = ['A', 'T', 'C', 'G'] var adn = 'testtest' adn += m[randInt(0, 4)] contains(adn, 'GAGA');").equals("null"); + code("var adn = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' var c = contains(adn, 'GAGA');").equals("null"); + code("var adn = '' for (var j = 0; j < 200; j++) { adn += 'A' } var c = contains(adn, 'GAGA');").equals("null"); + code("var count = 0 var adn = '' for (var j = 0; j < 200; j++) { adn += 'A' } var c = contains(adn, 'GAGA'); if (c) count++").equals("null"); + code("var m = ['A', 'T', 'C', 'G'] var count = 0 var adn = '' for (var j = 0; j < 200; j++) { adn += m[randInt(0, 4)] } var c = contains(adn, 'GAGA');").equals("null"); + code("var m = ['A', 'T', 'C', 'G'] var count = 0 var adn = '' for (var j = 0; j < 200; j++) { adn += m[randInt(0, 4)] }").equals("null"); + code("var count = 0 var adn = '' for (var j = 0; j < 200; j++) { adn += 'A' } var c = contains(adn, 'GAGA'); if (c) count++").equals("null"); + code("var count = 0 var m = ['A', 'T', 'C', 'G'] var adn = '' for (var j = 0; j < 200; j++) { adn += m[0] } var c = false if (c) count++").equals("null"); + code("var count = 0 var m = ['A', 'T', 'C', 'G'] var adn = '' adn += m[0] var c = contains(adn, 'GAGA'); if (c) count++").equals("null"); + code("var count = 0 var m = ['A', 'T', 'C', 'G'] var adn = '' for (var j = 0; j < 200; j++) { adn += m[0] } var c = contains(adn, 'GAGA'); if (c) count++").equals("null"); + code("var m = ['A', 'T', 'C', 'G'] var count = 0 var adn = '' for (var j = 0; j < 50; j++) { adn += m[randInt(0, 4)] } var c = true; if (c) count++").equals("null"); + code("var m = ['A', 'T', 'C', 'G'] var count = 0 var adn = '' for (var j = 0; j < 50; j++) { adn += m[randInt(0, 4)] } var c = false; if (c) count++").equals("null"); + code("var m = ['A', 'T', 'C', 'G'] var count = 0 var adn = '' for (var j = 0; j < 50; j++) { adn += m[randInt(0, 4)] } var c = contains(adn, 'GAGA'); if (c) count++").equals("null"); + code("var m = ['A', 'T', 'C', 'G'] var count = 0 var adn = '' for (var j = 0; j < 100; j++) { adn += m[randInt(0, 4)] } var c = contains(adn, 'GAGA'); if (c) count++").equals("null"); + code("var m = ['A', 'T', 'C', 'G'] var count = 0 var adn = '' for (var j = 0; j < 200; j++) { adn += m[randInt(0, 4)] } var c = contains(adn, 'GAGA'); if (c) count++").equals("null"); + code("var m = ['A', 'T', 'C', 'G'] var count = 0 var tests = 500 for (var k = 0; k < tests; k++) { var adn = '' for (var j = 0; j < 200; j++) { adn += m[randInt(0, 4)] } var c = contains(adn, 'GAGA'); if (c) count++ }").equals("null"); + code("var m = ['A', 'T', 'C', 'G'] var count = 0 var tests = 500 for (var k = 0; k < tests; k++) { var adn = '' for (var j = 0; j < 200; j++) { adn += m[randInt(0, 4)] } var c = contains(adn, 'GAGA'); if (c) count++ } return abs(100 * (count / tests) - 52) < 12;").equals("true"); + code_v1("var items = [[37, 3], [47, 10], [28, 5]] var all = []; var aux; aux = function(@current, i, tp, added, last) { if (count(current[1])) push(all, current); var item_count = count(items); for (var j = i; j < item_count; ++j) { var item = @items[j]; var item_id = item[0]; var cost = item[1]; if (cost > tp) continue;var new_added = added; new_added[item_id] = true; var copy = current; push(copy[1], @[item, cost, 1]); copy[0] += cost; aux(copy, j, tp - cost, new_added, item_id); } }; aux([0, []], 0, 25, [], -1); return count(all);").equals("44"); + } +} diff --git a/src/test/java/test/TestFunctions.java b/src/test/java/test/TestFunctions.java deleted file mode 100644 index 9358d424..00000000 --- a/src/test/java/test/TestFunctions.java +++ /dev/null @@ -1,588 +0,0 @@ -package test; - -import java.util.ArrayList; -import java.util.List; - -import leekscript.ErrorManager; -import leekscript.LSException; -import leekscript.compiler.LeekScript; -import leekscript.runner.AI; -import leekscript.runner.LeekConstants; -import leekscript.runner.values.AbstractLeekValue; -import leekscript.runner.values.ArrayLeekValue; -import leekscript.runner.values.BooleanLeekValue; -import leekscript.runner.values.DoubleLeekValue; -import leekscript.runner.values.IntLeekValue; -import leekscript.runner.values.NullLeekValue; -import leekscript.runner.values.StringLeekValue; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class TestFunctions { - - private AI ai; - - @Before - public void init() throws Exception { - ai = new TestAI(); - } - - @Test - public void MathFunctionsTest() throws Exception { - - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - // Test abs - codes.add("abs(-5)"); - values.add(5); - codes.add("abs(8)"); - values.add(8); - - // Test min - codes.add("min(8,5)"); - values.add(5); - codes.add("min(8,88)"); - values.add(8); - - // Test max - codes.add("max(8,5)"); - values.add(8); - codes.add("max(8,88)"); - values.add(88); - - // Test cos - codes.add("cos(0)"); - values.add(1); - codes.add("cos(2.5)"); - values.add(Math.cos(2.5)); - - // Test sin - codes.add("sin(0)"); - values.add(0); - codes.add("sin(2.5)"); - values.add(Math.sin(2.5)); - - // Test tan - codes.add("tan(0)"); - values.add(0); - codes.add("tan(2.5)"); - values.add(Math.tan(2.5)); - - // Test toRadians - codes.add("toRadians(180)"); - values.add(Math.PI); - - // Test toDegrees - codes.add("toDegrees(PI)"); - values.add(180); - - // Test acos - codes.add("acos(1)"); - values.add(0); - - // Test asin - codes.add("asin(0)"); - values.add(0); - - // Test atan - codes.add("atan(0)"); - values.add(0); - - // Test ceil - codes.add("ceil(1.897)"); - values.add(2); - codes.add("ceil(3.01)"); - values.add(4); - - // Test floor - codes.add("floor(1.897)"); - values.add(1); - codes.add("floor(3.01)"); - values.add(3); - - // Test round - codes.add("round(1.897)"); - values.add(2); - codes.add("round(3.01)"); - values.add(3); - - // Test sqrt - codes.add("sqrt(16)"); - values.add(4); - codes.add("sqrt(25)"); - values.add(5); - - // Test cqrt - codes.add("cbrt(1000)"); - values.add(10); - codes.add("cbrt(125)"); - values.add(5); - - // Test log - codes.add("log(1)"); - values.add(0); - codes.add("log(E)"); - values.add(1); - - // Test log10 - codes.add("log10(10)"); - values.add(1); - - // Test exp - codes.add("exp(1)"); - values.add(Math.E); - - // Test pow - codes.add("pow(5,3)"); - values.add(125); - - // Test signum - codes.add("signum(85)"); - values.add(1); - codes.add("signum(-0.5)"); - values.add(-1); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void StringFunctionsTest() throws Exception { - // Test AI - - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - // Test charAt - codes.add("charAt('bonjour', 1)"); - values.add("o"); - - // Test length - codes.add("length('bonjour')"); - values.add(7); - - // Test substring - codes.add("substring('bonjour',2,3)"); - values.add("njo"); - - // Test replace - codes.add("replace('bonjour','onj','pro')"); - values.add("bproour"); - - // Test indexOf - codes.add("indexOf('bonjour','o')"); - values.add(1); - codes.add("indexOf('bonjour','o',2)"); - values.add(4); - - // Test split - codes.add("string(split('1:2:3:4:5',':'))"); - values.add("[1, 2, 3, 4, 5]"); - codes.add("string(split('1:2:3:4:5',':',2))"); - values.add("[1, 2:3:4:5]"); - - // Test toLower - codes.add("toLower('AbCDefgh')"); - values.add("abcdefgh"); - - // Test toUpper - codes.add("toUpper('AbCDefgh')"); - values.add("ABCDEFGH"); - - // Test startsWith - codes.add("startsWith('bonjour','bon')"); - values.add(true); - codes.add("startsWith('bonjour','jour')"); - values.add(false); - - // Test endsWith - codes.add("endsWith('bonjour','bon')"); - values.add(false); - codes.add("endsWith('bonjour','jour')"); - values.add(true); - - // Test contains - codes.add("contains('bonjour','bon')"); - values.add(true); - codes.add("contains('bonjour','jour')"); - values.add(true); - codes.add("contains('bonjour','jourr')"); - values.add(false); - - // Test number - codes.add("contains('bonjour','bon')"); - values.add(true); - codes.add("contains('bonjour','jour')"); - values.add(true); - codes.add("contains('bonjour','jourr')"); - values.add(false); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void ArrayFunctionsTest() throws Exception { - // Test AI - - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - // Test remove - codes.add("function(){ var r = ['a','b','c','d','e']; return remove(r,1); }()"); - values.add("b"); - codes.add("function(){ var r = ['a','b','c','d','e']; return remove(r,55); }()"); - values.add(null); - codes.add("function(){ var r = ['a','b','c','d','e']; remove(r,1); return string(r);}()"); - values.add("[a, c, d, e]"); - - // Test count - codes.add("count(['a','b','c','d','e'])"); - values.add(5); - - // Test join - codes.add("join(['a','b','c','d','e'],'_')"); - values.add("a_b_c_d_e"); - codes.add("join(['a','b','c','d','e'],'_-')"); - values.add("a_-b_-c_-d_-e"); - - // Test insert - codes.add("function(){ var a = ['a','b','c','d']; insert(a, 'b', 1); return string(a); }()"); - values.add("[a, b, b, c, d]"); - - // Test push - codes.add("function(){ var a = ['a','b','c','d']; push(a, 'b'); return string(a); }()"); - values.add("[a, b, c, d, b]"); - - // Test unshift - codes.add("function(){ var a = ['a','b','c','d']; unshift(a, 'b'); return string(a); }()"); - values.add("[b, a, b, c, d]"); - - // Test shift - codes.add("function(){ var a = ['a','b','c','d']; shift(a); return string(a); }()"); - values.add("[b, c, d]"); - - // Test pop - codes.add("function(){ var a = ['a','b','c','d']; pop(a); return string(a); }()"); - values.add("[a, b, c]"); - - // Test removeElement - codes.add("function(){ var a = ['a','b','c','d']; removeElement(a,'c'); return string(a); }()"); - values.add("[0 : a, 1 : b, 3 : d]"); - - // Test removeKey - codes.add("function(){ var a = ['a':'va','b':'vb','c':'vc','d':'vd']; removeKey(a,'a'); return string(a); }()"); - values.add("[b : vb, c : vc, d : vd]"); - - // Test sort - codes.add("function(){ var a = [8,6,2,3,7,1,0]; sort(a); return string(a); }()"); - values.add("[0, 1, 2, 3, 6, 7, 8]"); - codes.add("function(){ var a = [8,6,2,3,7,1,0]; sort(a, SORT_ASC); return string(a); }()"); - values.add("[0, 1, 2, 3, 6, 7, 8]"); - codes.add("function(){ var a = [8,6,2,3,7,1,0]; sort(a, SORT_DESC); return string(a); }()"); - values.add("[8, 7, 6, 3, 2, 1, 0]"); - - // Test assocSort - codes.add("function(){ var a = ['b':'vb','c':'vc','a':'va','d':'vd']; assocSort(a); return string(a); }()"); - values.add("[a : va, b : vb, c : vc, d : vd]"); - codes.add("function(){ var a = ['b':'vb','c':'vc','a':'va','d':'vd']; assocSort(a, SORT_DESC); return string(a); }()"); - values.add("[d : vd, c : vc, b : vb, a : va]"); - codes.add("function(){ var a = [8,6,2,3,7,1,0]; assocSort(a); return string(a); }()"); - values.add("[6 : 0, 5 : 1, 2 : 2, 3 : 3, 1 : 6, 4 : 7, 0 : 8]"); - - // Test keySort - codes.add("function(){ var a = ['b':'vb','c':'vc','a':'va','d':'vd']; keySort(a); return string(a); }()"); - values.add("[a : va, b : vb, c : vc, d : vd]"); - codes.add("function(){ var a = ['b':'vb','c':'vc','a':'va','d':'vd']; keySort(a, SORT_DESC); return string(a); }()"); - values.add("[d : vd, c : vc, b : vb, a : va]"); - codes.add("function(){ var a = [6 : 0, 5 : 1, 2 : 2, 3 : 3, 1 : 6, 4 : 7, 0 : 8]; keySort(a); return string(a); }()"); - values.add("[8, 6, 2, 3, 7, 1, 0]"); - - // Test search - codes.add("function(){ var a = ['a','b','c','d']; return search(a,'c'); }()"); - values.add(2); - codes.add("function(){ var a = ['cle1':'a','cle2':'b','cle3':'c','cle4':'d']; return search(a,'c'); }()"); - values.add("cle3"); - codes.add("function(){ var a = ['cle1':'a','cle2':'b','cle3':'c','cle4':'d']; return search(a,'454'); }()"); - values.add(null); - - // Test inArray - codes.add("function(){ var a = ['a','b','c','d']; return inArray(a,'c'); }()"); - values.add(true); - codes.add("function(){ var a = ['cle1':'a','cle2':'b','cle3':'c','cle4':'d']; return inArray(a,'c'); }()"); - values.add(true); - codes.add("function(){ var a = ['cle1':'a','cle2':'b','cle3':'c','cle4':'d']; return inArray(a,'cle3'); }()"); - values.add(false); - codes.add("function(){ var a = ['cle1':'a','cle2':'b','cle3':'c','cle4':'d']; return inArray(a,'454'); }()"); - values.add(false); - - // Test reverse - codes.add("function(){ var a = ['a','b','c','d']; reverse(a); return string(a); }()"); - values.add("[d, c, b, a]"); - - // Test arrayMin - codes.add("arrayMin([8,4,3,-1,8,44])"); - values.add(-1); - codes.add("arrayMin([0:7,8:9,'a':2])"); - values.add(2); - - // Test arrayMax - codes.add("arrayMax([8,4,3,-1,8,44])"); - values.add(44); - codes.add("arrayMax([0:7,8:9,'a':2])"); - values.add(9); - - // Test sum - codes.add("sum([1,5,7])"); - values.add(13); - codes.add("sum([0:1,'a':5,'test':7])"); - values.add(13); - codes.add("sum([])"); - values.add(0); - - // Test average - codes.add("average([2,4,6])"); - values.add(4); - codes.add("average([0:2,'a':4,'test':6])"); - values.add(4); - codes.add("average([])"); - values.add(0); - - // Test fill - codes.add("function(){ var a = [1,2,3]; fill(a, 'a'); return string(a); }()"); - values.add("[a, a, a]"); - codes.add("function(){ var a = [1,2,3]; fill(a, 'a',2); return string(a); }()"); - values.add("[a, a, 3]"); - codes.add("function(){ var a = []; fill(a, 'a',2); return string(a); }()"); - values.add("[a, a]"); - - // Test isEmpty - codes.add("isEmpty([2,4,6])"); - values.add(false); - codes.add("isEmpty([2:8])"); - values.add(false); - codes.add("isEmpty([])"); - values.add(true); - - // Test subArray - codes.add("string(subArray([1,2,3,4,5,6,7,8],1,3))"); - values.add("[2, 3, 4]"); - codes.add("string(subArray([1,2,3,4,5,6,7,8],3,3))"); - values.add("[4]"); - - // Test pushAll - codes.add("function(){ var a = [1,2,3]; pushAll(a, [5,6,7]); return string(a); }()"); - values.add("[1, 2, 3, 5, 6, 7]"); - - // Test assocReverse - codes.add("function(){ var a = [1,2,3]; assocReverse(a); return string(a); }()"); - values.add("[2 : 3, 1 : 2, 0 : 1]"); - - // Test large array - codes.add("function(){ var a = []; for (var i = 0; i < 100000; ++i) { push(a, i); } return a[91212]; }()"); - values.add("91212"); - - // Test arrayMap et suivant => plus loin - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void stringTest() throws Exception { - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - // Test nombre - codes.add("'\\\\'"); - values.add("\\\\"); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void typeOfTest() throws Exception { - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - // Test nombre - codes.add("typeOf(255)"); - values.add(LeekConstants.TYPE_NUMBER.getIntValue()); - codes.add("typeOf(255.8)"); - values.add(LeekConstants.TYPE_NUMBER.getIntValue()); - // Test string - codes.add("typeOf('coucou')"); - values.add(LeekConstants.TYPE_STRING.getIntValue()); - // Test boolean - codes.add("typeOf(false)"); - values.add(LeekConstants.TYPE_BOOLEAN.getIntValue()); - // Test array - codes.add("typeOf([1,false])"); - values.add(LeekConstants.TYPE_ARRAY.getIntValue()); - // Test fonction - codes.add("typeOf(function(){ return null; })"); - values.add(LeekConstants.TYPE_FUNCTION.getIntValue()); - // Test null - codes.add("typeOf(null)"); - values.add(LeekConstants.TYPE_NULL.getIntValue()); - // Test piège - codes.add("typeOf(function(){ return 4; }())"); - values.add(LeekConstants.TYPE_NUMBER.getIntValue()); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void colorTest() throws Exception { - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - // Test color - codes.add("color(255,0,255)"); - values.add(0xFF00FF); - // Test color - codes.add("color(255,255,0)"); - values.add(0xFFFF00); - // Test color - codes.add("color(0,255,255)"); - values.add(0x00FFFF); - - // Red - codes.add("getRed(" + 0xAE0000 + ")"); - values.add(174); - // Green - codes.add("getGreen(" + 0xAF00 + ")"); - values.add(175); - // Blue - codes.add("getBlue(" + 0xAD + ")"); - values.add(173); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void sortTest() throws Exception { - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - // Test sort - codes.add("function(){ var t =[null,null,4,8,9]; sort(t); return t;}()"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(4), new IntLeekValue(8), new IntLeekValue(9), new NullLeekValue(), new NullLeekValue() })); - - // Test sort - codes.add("function(){ var t =[4, null, 4, null, 4]; sort(t); return t;}()"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(4), new IntLeekValue(4), new IntLeekValue(4), new NullLeekValue(), new NullLeekValue() })); - - // Test sort desc - codes.add("function(){ var t =[4, null, 5, null, 8]; sort(t,SORT_DESC); return t;}()"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { new NullLeekValue(), new NullLeekValue(), new IntLeekValue(8), new IntLeekValue(5), new IntLeekValue(4) })); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void testReference() throws Exception { - String leekscript = "var t = [3,4,5]; t[3]=[1,2,3,4]; var r = @t[3]; r[4] ='prou3t'; return t;"; - LeekScript.testScript(leekscript, new NullLeekValue()); - } - - @Test - public void testAnonymousFunctioNSelfAccess() { - String leekscript = "var t = function(){ return t; };"; - try { - LeekScript.testScript(leekscript, new NullLeekValue()); - } catch (Exception e) { - - ErrorManager.exception(e); - } - } - - @Test - public void testIfIfNot() { - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("function(){ var a = 1; if(a is 1) return 2; else return 0;}()"); - values.add(2); - - codes.add("function(){ var a = 1; if(a is 2) return 2; else return 0;}()"); - values.add(0); - - codes.add("function(){ var a = 1; if(a is not 2) return 2; else return 0;}()"); - values.add(2); - - codes.add("function(){ var a = 1; if(a is not 1) return 2; else return 0;}()"); - values.add(0); - - codes.add("function(){ var a = true; if(not a) return 2; else return 0;}()"); - values.add(0); - - // Test AI - try { - Assert.assertTrue(testAI(codes, values)); - } catch (Exception e) { - e.printStackTrace(); - Assert.fail(); - } - } - - @Test - public void testOperators() { - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("function(){ var a = 1; var result = -10 + (1- (a-1)); return result;}()"); - values.add(-9); - - codes.add("function(){ var a = 1; var result = 0; result = -10 + (1- (a-1)); return result;}()"); - values.add(-9); - // Test AI - try { - Assert.assertTrue(testAI(codes, values)); - } catch (Exception e) { - e.printStackTrace(); - Assert.fail(); - } - } - - private boolean testAI(List mCodes, List mValues) throws Exception { - String leekscript = "return ["; - AbstractLeekValue[] values = new AbstractLeekValue[mValues.size()]; - - for (int i = 0; i < mValues.size(); i++) { - if (i != 0) - leekscript += ","; - leekscript += mCodes.get(i); - Object c = mValues.get(i); - if (c == null) - values[i] = new NullLeekValue(); - else if (c instanceof Integer) - values[i] = new IntLeekValue(((Integer) mValues.get(i))); - else if (c instanceof Double) - values[i] = new DoubleLeekValue(((Double) mValues.get(i))); - else if (c instanceof String) - values[i] = new StringLeekValue(((String) mValues.get(i))); - else if (c instanceof Boolean) - values[i] = new BooleanLeekValue(((Boolean) mValues.get(i))); - else if (c instanceof AbstractLeekValue) - values[i] = (AbstractLeekValue) mValues.get(i); - } - leekscript += "];"; - try { - return LeekScript.testScript(leekscript, new ArrayLeekValue(ai, values)); - } catch (LSException e) { - int i = e.getIndex(); - System.err.println("Erreur :\n" + mCodes.get(i)); - System.err.println("Valeur attendue :\n" + e.getThe().getString(ai)); - System.err.println("Valeur renvoyée :\n" + e.getRun().getString(ai)); - return false; - } - } -} diff --git a/src/test/java/test/TestGeneral.java b/src/test/java/test/TestGeneral.java index 08f3f73a..eedb8b77 100644 --- a/src/test/java/test/TestGeneral.java +++ b/src/test/java/test/TestGeneral.java @@ -1,666 +1,156 @@ package test; -import leekscript.LSException; -import leekscript.compiler.LeekScript; -import leekscript.compiler.exceptions.LeekCompilerException; -import leekscript.functions.VariableOperations; import leekscript.runner.LeekConstants; -import leekscript.runner.values.AbstractLeekValue; -import leekscript.runner.values.ArrayLeekValue; -import leekscript.runner.values.BooleanLeekValue; -import leekscript.runner.values.DoubleLeekValue; -import leekscript.runner.values.IntLeekValue; -import leekscript.runner.values.NullLeekValue; -import leekscript.runner.values.StringLeekValue; +import leekscript.common.Error; +import leekscript.compiler.WordParser; -import java.util.ArrayList; -import java.util.List; +public class TestGeneral extends TestCommon { -import org.junit.Assert; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; + public void run() { -import com.alibaba.fastjson.JSONObject; - -public class TestGeneral { - - TestAI ai; - - @Before - public void init() throws Exception { - ai = new TestAI(); - } - - @Test - public void lsOperationsTest() throws Exception { - VariableOperations op = new VariableOperations(JSONObject.parseObject("{\"1\":\"50\",\"10\":\"100\",\"50\":\"1000\",\"100\":\"5000\"}")); - System.out.println(op.getOperations(501)); - } - - @Test - public void ternaireBisTest() throws Exception { - try { - LeekScript.testScript("return (1&2?'coucou');", new BooleanLeekValue(false)); - Assert.fail("Compilation validée..."); - } catch (LeekCompilerException exp) { - return; - } catch (Exception e) { - Assert.fail("Compilation validée..."); - } - } - - @Test - public void incrementTest() throws Exception { - Assert.assertTrue(testScript("var u=0; return 1*u++ +2-2;", new IntLeekValue(0))); - } - - @Test - public void decrementTest() throws Exception { - Assert.assertTrue(testScript("var u=2; return 1*u--+2-2;", new IntLeekValue(2))); - } - - @Test - public void preIncrementTest() throws Exception { - Assert.assertTrue(testScript("var u=0; return 1*++u+2-2;", new IntLeekValue(1))); - } - - @Test - public void preDecrementTest() throws Exception { - Assert.assertTrue(testScript("var u=2; return 1*--u+2-2;", new IntLeekValue(1))); - } - - @Test - public void tripleEqualsTest() throws Exception { - Assert.assertTrue(testScript("return 1 === true;", new BooleanLeekValue(false))); - } - - @Test - public void ternaireTest() throws Exception { - Assert.assertTrue(testScript("var t = true; return t === 2?6:2*4;", 8)); - } - - @Test - public void anonymousFunctionTest() throws Exception { - Assert.assertTrue(testScript("var test = function(@a){ a = 8; }; var test2 = function(b, d){ var c = 1; d(c); return c; }; return test2(1, test);", 8)); - } - - @Test - public void conditionTest() throws Exception { - Assert.assertTrue(testScript("var test = 8; if(test == 7) return 1; else if(test == 8) return 2; else return 3;", new IntLeekValue(2))); - } - - @Test - public void foreachTest() throws Exception { - Assert.assertTrue(testScript("var test = [0,1,2,3,4,5]; var retour = \"\"; for(var i in test){ retour += i; } return retour;", - new StringLeekValue("012345"))); - } - - @Test - public void foreachGlobalTest() throws Exception { - Assert.assertTrue(testScript("global i; var test = [0,1,2,3,4,5]; var retour = \"\"; for(i in test){ retour += i; } return retour;", - new StringLeekValue("012345"))); - } - - @Test - public void foreachkeyvalTest() throws Exception { - Assert.assertTrue(testScript("var test = ['a':5,'b':8,'c':8,9:'p']; var retour = \"\"; for(var i : var j in test){ retour += i+':'+j; } return retour;", - new StringLeekValue("a:5b:8c:89:p"))); - } - - @Test - public void foreachkeyvalGlobalTest() throws Exception { - Assert.assertTrue(testScript("global i,j; var test = ['a':5,'b':8,'c':8,9:'p']; var retour = \"\"; for(i : j in test){ retour += i+':'+j; } return retour;", - new StringLeekValue("a:5b:8c:89:p"))); - } - - @Test - public void forTest() throws Exception { - Assert.assertTrue(testScript("for(var i=0;i<1000;i++){}", new NullLeekValue())); - } - - @Test - public void forGlobalTest() throws Exception { - Assert.assertTrue(testScript("global i; for(i=0;i<1000;i++){}", new NullLeekValue())); - } - - @Test - public void functionGlobalTest() throws Exception { - Assert.assertTrue(testScript("function testi() { var tab=[1,5]; for (i in tab) debug(i); } global i;", new NullLeekValue())); - } - - @Test - public void whileTest() throws Exception { - Assert.assertTrue(testScript("var i = 0; while(i<1000){ i++; }", new NullLeekValue())); - } - - @Test - public void dowhileTest() throws Exception { - Assert.assertTrue(testScript("var i = 0; do{ i++; } while(i < 1000);", new NullLeekValue())); - } - - // Test simples d'opérateurs - @Test - public void additionTest() throws Exception { - Assert.assertTrue(testScript("var test = 1 + 8; test += 3; test++; var a = 7; var b = 1; return [test, a+b, (1+1)+9.5, 'test'+8];", - new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(13), new IntLeekValue(8), new DoubleLeekValue(11.5), - new StringLeekValue("test8") }))); - } - - @Test - public void soustractionTest() throws Exception { - Assert.assertTrue(testScript("var test = 20 - 8; test -= 3; test--; var a = 7; var b = 1; return [test, a-b, (1-1)-9.5];", - new ArrayLeekValue(ai, new AbstractLeekValue[] { - new IntLeekValue(8), new IntLeekValue(6), new DoubleLeekValue(-9.5) }))); - } - - @Test - public void multiplicationTest() throws Exception { - Assert.assertTrue(testScript("var test = 4; test *= 3; var c = 7;return [test, 8*9, 2*c];", - new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(12), - new IntLeekValue(72), new DoubleLeekValue(14) }))); - } - - @Test - public void modulusTest() throws Exception { - Assert.assertTrue(testScript("var test = 4; test %= 3; var c = 7;return [test, 8%9, 8%c];", - new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(1), - new IntLeekValue(8), new DoubleLeekValue(1) }))); - } - - @Test - public void divisionTest() throws Exception { - Assert.assertTrue(testScript("var test = 7; test /= 2; var c = 7;return [test, 12/6, 14/c];", - new ArrayLeekValue(ai, new AbstractLeekValue[] { new DoubleLeekValue(3.5), - new IntLeekValue(2), new DoubleLeekValue(2) }))); - } - - @Test - public void operatorsTest() throws Exception { - AbstractLeekValue[] values = new AbstractLeekValue[] { new BooleanLeekValue(true), new BooleanLeekValue(false), - new BooleanLeekValue(true), new BooleanLeekValue(false), - - new BooleanLeekValue(false), new BooleanLeekValue(true), new BooleanLeekValue(false) }; - Assert.assertTrue(testScript("return [true && true, true && false, true || false, false || false, 1 > 3, 1 < 3, 4 == 7];", - new ArrayLeekValue(ai, values))); - } - - @Test - public void prioritiesTest() throws Exception { - AbstractLeekValue[] values = new AbstractLeekValue[] { - // 1+6*2 - 3*2 - new IntLeekValue(7), - // 7*8 - 1 - new IntLeekValue(55), - // 3*3 - 8 > 0 - new BooleanLeekValue(true), - // 7*8 == 56 && 33 -8 - new BooleanLeekValue(true) }; - Assert.assertTrue(testScript("return [1+6*2-3*2,7*8-1, 3*3-8>0, 7*8 == 56 && 33-8 ];", - new ArrayLeekValue(ai, values))); - } - - // Test un peu plus poussés - @Test - public void functionTest() throws Exception { - Assert.assertTrue(testScript("function test(a){ return a+2; } return test(7);", 9)); - } - - @Test - public void divisionByZeroTest() throws Exception { - Assert.assertTrue(testScript("return 8/0;", null)); - Assert.assertTrue(testScript("return 8/null;", null)); - } - - @Test - public void whileReturnTest() throws Exception { - Assert.assertTrue(LeekScript.testScript("var t = 0; while(t<5){ t++; return t;}", new IntLeekValue(1))); - Assert.assertTrue(LeekScript.testScript("var t = 0; while(t<5){ t++; return t;} return 0;", new IntLeekValue(1))); - } - - @Test - public void doWhileReturnTest() throws Exception { - Assert.assertTrue(LeekScript.testScript("var t = 0; do{ t++; return t;}while(t<5);", new IntLeekValue(1))); - try { - LeekScript.testScript("var t = 0; do{ t++; return t;}while(t<5); return 2;", new IntLeekValue(1)); - Assert.fail("Compilation validée..."); - } catch (LeekCompilerException e) { - return; - } catch (Exception e) { - Assert.fail("Compilation validée..."); - } - } - - @Test - public void forReturnTest() throws Exception { - Assert.assertTrue(LeekScript.testScript("for(var i=0;i<3;i++){ return i; }", new IntLeekValue(0))); - Assert.assertTrue(LeekScript.testScript("for(var i=0;i<3;i++){ return i; } return 2;", new IntLeekValue(0))); - } - - @Test - public void anonymousTest() throws Exception { - Assert.assertTrue(LeekScript.testScript("function te(a){ return function(){ return a**2; }; } return te(2)();", new IntLeekValue(4))); - } - - @Test - public void anonymous2Test() throws Exception { - Assert.assertTrue(LeekScript.testScript("function te(a){ return function(b){ return function(c){return a*b*c;}; }; } return te(2)(1)(2);", new IntLeekValue(4))); - } - - @Test - public void anonymous3Test() throws Exception { - Assert.assertTrue(LeekScript.testScript("var tab = [2,3,4,5,6];var r = [];for(var i: var j in tab){ r[i] = function(){ return j; };}return 4;", new IntLeekValue(4))); - } - - @Test - public void conditionalTest() throws Exception { - Assert.assertTrue(LeekScript.testScript("var test = 0; if(false) if(true) test = 3; else test = 1; return test;", new IntLeekValue(0))); - } - - @Test - public void forEachReturnTest() throws Exception { - Assert.assertTrue(LeekScript.testScript("var tab = [0,1,2,3]; for(var i in tab){ return i; }", new IntLeekValue(0))); - Assert.assertTrue(LeekScript.testScript("var tab = [0,1,2,3]; for(var i in tab){ return i; } return 5;", new IntLeekValue(0))); - } - - @Test - public void forEachKeyReturnTest() throws Exception { - Assert.assertTrue(LeekScript.testScript("var tab = [1:0,2:1,3:2,4:3]; for(var i : var j in tab){ return i; } ", new IntLeekValue(1))); - Assert.assertTrue(LeekScript.testScript("var tab = [1:0,2:1,3:2,4:3]; for(var i : var j in tab){ return i; } return 0;", new IntLeekValue(1))); - } - - @Test - public void arrayMapTest() throws Exception { - - Assert.assertTrue(LeekScript.testScript("return arrayMap([1,2,3,4,5],function(e){ return e*2; });", new ArrayLeekValue(ai, new AbstractLeekValue[] { - new IntLeekValue(2), new IntLeekValue(4), new IntLeekValue(6), new IntLeekValue(8), new IntLeekValue(10) }))); - - Assert.assertTrue(LeekScript.testScript("return arrayMap([4,9,16],sqrt);", new ArrayLeekValue(ai, new AbstractLeekValue[] { - new IntLeekValue(2), new IntLeekValue(3), new IntLeekValue(4) }))); - - Assert.assertTrue(LeekScript.testScript("return arrayMap(['a':1,'b':2],function(k,v){ return k+v;});", new ArrayLeekValue(ai, new AbstractLeekValue[] { - new StringLeekValue("a"), new StringLeekValue("a1"), - new StringLeekValue("b"), new StringLeekValue("b2") }, true))); - - Assert.assertTrue(LeekScript.testScript("return function(){ var t = ['a':1,'b':2]; arrayMap(t,function(@k,@v){ v='tomate';k='ctus'; return 3;}); return t;}();", new ArrayLeekValue(ai, new AbstractLeekValue[] { new StringLeekValue("a"), - new StringLeekValue("tomate"), - new StringLeekValue("b"), - new StringLeekValue("tomate") }, true))); - } - - @Test - public void arrayFilterTest() throws Exception { - - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("arrayFilter([1,2,3,4,5,6,7,8,9],function(e){ return e>5; })"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { - new IntLeekValue(5), new IntLeekValue(6), new IntLeekValue(6), - new IntLeekValue(7), new IntLeekValue(7), - new IntLeekValue(8), new IntLeekValue(8), new IntLeekValue(9) }, true)); - - codes.add("arrayFilter([4,5,6,'test',8,9],function(e){ return e=='test'; })"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(3), new StringLeekValue("test") }, true)); - - codes.add("string(arrayFilter(['a','b','c','d'],function(k,v){ return k==3; }))"); - values.add("[3 : d]"); - - codes.add("string(function(){ var t = ['a','b','c','d']; arrayFilter(t,function(k,@v){ v=4; return k==3; }); return t;}())"); - values.add("[4, 4, 4, 4]"); - - codes.add("string(arrayFilter(['a','b','c','d'],function(k,@v){ v=4; return k==3; }))"); - values.add("[3 : 4]"); - - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void arrayFlatten() throws Exception { - - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("arrayFlatten([6,7,[8,9]],99)"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { - new IntLeekValue(6), new IntLeekValue(7), new IntLeekValue(8), new IntLeekValue(9) })); - - codes.add("arrayFlatten([6,[[7]],[8,9]],2)"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { - new IntLeekValue(6), new IntLeekValue(7), new IntLeekValue(8), new IntLeekValue(9) })); - - codes.add("arrayFlatten([6,[[7]],[8,9]])"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { - new IntLeekValue(6), new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(7) }), - new IntLeekValue(8), new IntLeekValue(9) })); - - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void arrayFoldLeft() throws Exception { - - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("arrayFoldLeft([6,7,8,9], function(a,b){return a+b;},0)"); - values.add(30); - - codes.add("arrayFoldLeft([1,0,1,2,5,7,9], function(a,b){return a+','+b;},'')"); - values.add(",1,0,1,2,5,7,9"); - - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void arrayFoldRight() throws Exception { - - // On lance le test - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("arrayFoldRight([6,7,8,9], function(a,b){return a+b;},0)"); - values.add(30); - - codes.add("arrayFoldRight([1,0,1,2,5,7,9], function(a,b){return a+','+b;},'')"); - values.add("1,0,1,2,5,7,9,"); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void arrayPartition() throws Exception { - - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("arrayPartition([6,7,8,9], function(a){return a&1;})"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { - new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(1), new IntLeekValue(7), new IntLeekValue(3), new IntLeekValue(9) }, true), - new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(0), new IntLeekValue(6), new IntLeekValue(2), new IntLeekValue(8) }, true) })); - - codes.add("string(arrayPartition([6,7,8,9], function(k,v){return k;}))"); - values.add("[[1 : 7, 2 : 8, 3 : 9], [6]]"); - - codes.add("string(arrayPartition([4,3,2,1], function(k,v){return k codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("string([0]+[1,2])"); - values.add("[0, 1, 2]"); - - codes.add("function(){ var a = [0,1]; a+= [3]; return string(a);}()"); - values.add("[0, 1, 3]"); - - codes.add("string(arrayConcat([0],[1,2]))"); - values.add("[0, 1, 2]"); - - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void arrayIter() throws Exception { - - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("string(function(){ var t = [1,2,3,4]; arrayIter(t, function(v){ v=2; }); return t; }())"); - values.add("[1, 2, 3, 4]"); - - codes.add("string(function(){ var t = [1,2,3,4]; arrayIter(t, function(@v){ v=2; }); return t; }())"); - values.add("[2, 2, 2, 2]"); - - codes.add("string(function(){ var t = [1,2,3,4]; arrayIter(t, function(k, @v){ v=k; }); return t; }())"); - values.add("[0, 1, 2, 3]"); - - codes.add("string(function(){ var t = [1,2,3,4]; arrayIter(t, function(k, v){ v=k; }); return t; }())"); - values.add("[1, 2, 3, 4]"); - - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void arraySort() throws Exception { - - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("string(function(){var t = [0,1,2]; return arraySort(t,function(e, f){return (e>f)?(-1):(ek2)?(-1):(k1k2)?(-1):(k1 codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("function(){ var a = 1; if(a is 1) return 2; else return 0;}()"); - values.add(2); - - codes.add("function(){ var a = 1; if(a is 2) return 2; else return 0;}()"); - values.add(0); - - codes.add("function(){ var a = 1; if(a is not 2) return 2; else return 0;}()"); - values.add(2); - - codes.add("function(){ var a = 1; if(a is not 1) return 2; else return 0;}()"); - values.add(0); - - codes.add("function(){ var a = true; if(not a) return 2; else return 0;}()"); - values.add(0); - - // Test AI - try { - Assert.assertTrue(testAI(codes, values)); - } catch (Exception e) { - e.printStackTrace(); - Assert.fail(); - } - } - - @Test - public void testOperators() { - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - codes.add("function(){ var a = 1; var result = -10 + (1- (a-1)); return result;}()"); - values.add(-9); - - codes.add("function(){ var a = 1; var result = 0; result = -10 + (1- (a-1)); return result;}()"); - values.add(-9); - // Test AI - try { - Assert.assertTrue(testAI(codes, values)); - } catch (Exception e) { - e.printStackTrace(); - Assert.fail(); - } - } - - @Test - public void sortTest() throws Exception { - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - // Test sort - codes.add("function(){ var t =[null,null,4,8,9]; sort(t); return t;}()"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(4), new IntLeekValue(8), new IntLeekValue(9), new NullLeekValue(), new NullLeekValue() })); - - // Test sort - codes.add("function(){ var t =[4, null, 4, null, 4]; sort(t); return t;}()"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(4), new IntLeekValue(4), new IntLeekValue(4), new NullLeekValue(), new NullLeekValue() })); - - // Test sort desc - codes.add("function(){ var t =[4, null, 5, null, 8]; sort(t,SORT_DESC); return t;}()"); - values.add(new ArrayLeekValue(ai, new AbstractLeekValue[] { new NullLeekValue(), new NullLeekValue(), new IntLeekValue(8), new IntLeekValue(5), new IntLeekValue(4) })); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - @Test - public void colorTest() throws Exception { - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); - - // Test color - codes.add("color(255,0,255)"); - values.add(0xFF00FF); - // Test color - codes.add("color(255,255,0)"); - values.add(0xFFFF00); - // Test color - codes.add("color(0,255,255)"); - values.add(0x00FFFF); - - // Red - codes.add("getRed(" + 0xAE0000 + ")"); - values.add(174); - // Green - codes.add("getGreen(" + 0xAF00 + ")"); - values.add(175); - // Blue - codes.add("getBlue(" + 0xAD + ")"); - values.add(173); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - - @Test - public void typeOfTest() throws Exception { - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); + section("null"); + code_v1_2("return null").equals("null"); + code_v1_2("return Null").equals("null"); + code_v1_2("return NULL").equals("null"); + code_v3_("return null").equals("null"); + code_v3_("return Null").equals(""); + code_v3_("return NULL").error(Error.UNKNOWN_VARIABLE_OR_FUNCTION); + section("typeOf()"); // Test nombre - codes.add("typeOf(255)"); - values.add(LeekConstants.TYPE_NUMBER.getIntValue()); - codes.add("typeOf(255.8)"); - values.add(LeekConstants.TYPE_NUMBER.getIntValue()); + code("return typeOf(255)").equals(String.valueOf(LeekConstants.TYPE_NUMBER.getIntValue())); + code("return typeOf(255.8)").equals(String.valueOf(LeekConstants.TYPE_NUMBER.getIntValue())); // Test string - codes.add("typeOf('coucou')"); - values.add(LeekConstants.TYPE_STRING.getIntValue()); + code("return typeOf('coucou')").equals(String.valueOf(LeekConstants.TYPE_STRING.getIntValue())); // Test boolean - codes.add("typeOf(false)"); - values.add(LeekConstants.TYPE_BOOLEAN.getIntValue()); + code("return typeOf(false)").equals(String.valueOf(LeekConstants.TYPE_BOOLEAN.getIntValue())); // Test array - codes.add("typeOf([1,false])"); - values.add(LeekConstants.TYPE_ARRAY.getIntValue()); + code("return typeOf([1,false])").equals(String.valueOf(LeekConstants.TYPE_ARRAY.getIntValue())); // Test fonction - codes.add("typeOf(function(){ return null; })"); - values.add(LeekConstants.TYPE_FUNCTION.getIntValue()); + code("return typeOf(function(){ return null; })").equals(String.valueOf(LeekConstants.TYPE_FUNCTION.getIntValue())); // Test null - codes.add("typeOf(null)"); - values.add(LeekConstants.TYPE_NULL.getIntValue()); + code("return typeOf(null)").equals(String.valueOf(LeekConstants.TYPE_NULL.getIntValue())); // Test piège - codes.add("typeOf(function(){ return 4; }())"); - values.add(LeekConstants.TYPE_NUMBER.getIntValue()); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } + code("return typeOf(function(){ return 4; }())").equals(String.valueOf(LeekConstants.TYPE_NUMBER.getIntValue())); - @Test - public void stringTest() throws Exception { - ArrayList codes = new ArrayList(); - ArrayList values = new ArrayList(); + section("color()"); + code("return color(255,0,255)").equals(String.valueOf(0xFF00FF)); + code("return color(255,255,0)").equals(String.valueOf(0xFFFF00)); + code("return color(0,255,255)").equals(String.valueOf(0x00FFFF)); - // Test nombre - codes.add("'\\\\'"); - values.add("\\\\"); - - // Test AI - Assert.assertTrue(testAI(codes, values)); - } - - private boolean testAI(List mCodes, List mValues) throws Exception { - String leekscript = "return ["; - AbstractLeekValue[] values = new AbstractLeekValue[mValues.size()]; - - for (int i = 0; i < mValues.size(); i++) { - if (i != 0) - leekscript += ","; - leekscript += mCodes.get(i); - Object c = mValues.get(i); - if (c instanceof Integer) - values[i] = new IntLeekValue(((Integer) mValues.get(i))); - else if (c instanceof Double) - values[i] = new DoubleLeekValue(((Double) mValues.get(i))); - else if (c instanceof String) - values[i] = new StringLeekValue(((String) mValues.get(i))); - else if (c instanceof Boolean) - values[i] = new BooleanLeekValue(((Boolean) mValues.get(i))); - else if (c instanceof AbstractLeekValue) - values[i] = (AbstractLeekValue) mValues.get(i); - else - values[i] = new NullLeekValue(); + // Red + code("return getRed(" + 0xAE0000 + ")").equals("174"); + // Green + code("return getGreen(" + 0xAF00 + ")").equals("175"); + // Blue + code("return getBlue(" + 0xAD + ")").equals("173"); + + section("Variables with keywords"); + for (var word : WordParser.reservedWords) { + if (word.equals("this")) { + code_v1("var " + word + " = 2;").error(Error.NONE); + code_v2("var " + word + " = 2;").error(Error.THIS_NOT_ALLOWED_HERE); + code_v3_("var " + word + " = 2;").error(Error.VARIABLE_NAME_UNAVAILABLE); + } else if (word.equals("instanceof")) { + code_v1_2("var " + word + " = 2;").error(Error.VAR_NAME_EXPECTED); + code_v3_("var " + word + " = 2;").error(Error.VAR_NAME_EXPECTED); + } else if (word.equals("function")) { + code_v1_2("var " + word + " = 2;").error(Error.OPENING_PARENTHESIS_EXPECTED); + code_v3_("var " + word + " = 2;").error(Error.OPENING_PARENTHESIS_EXPECTED); + } else { + code_v1_2("var " + word + " = 2;").error(Error.NONE); + code_v3_("var " + word + " = 2;").error(Error.VARIABLE_NAME_UNAVAILABLE); + } } - leekscript += "];"; - try { - return LeekScript.testScript(leekscript, new ArrayLeekValue(ai, values)); - } catch (LSException e) { - System.err.println("Erreur :\n" + leekscript); - System.err.println("Valeur attendue :\n" + e.getThe().getString(ai)); - System.err.println("Valeur renvoyée :\n" + e.getRun().getString(ai)); - return false; + section("Globals with keywords"); + code_v1_2("global break = 2").error(Error.VARIABLE_NAME_UNAVAILABLE); + for (var word : WordParser.reservedWords) { + if (word.equals("this")) { + code_v3_("global " + word + " = 2;").error(Error.VARIABLE_NAME_UNAVAILABLE); + } else if (word.equals("instanceof")) { + code_v3_("global " + word + " = 2;").error(Error.VAR_NAME_EXPECTED_AFTER_GLOBAL); + } else if (word.equals("function")) { + code_v3_("global " + word + " = 2;").error(Error.VARIABLE_NAME_UNAVAILABLE); + } else { + code_v3_("global " + word + " = 2;").error(Error.VARIABLE_NAME_UNAVAILABLE); + } } - } - - private boolean testScript(String leekscript, AbstractLeekValue value) throws Exception { - if (value == null) - value = new NullLeekValue(); - - return LeekScript.testScript(leekscript, value); - } - private boolean testScript(String leekscript, int value) throws Exception { - return testScript(leekscript, new IntLeekValue(value)); + code_v1("var new = 12 var b = @new return b").equals("12"); + code_v1("global final = 2").error(Error.NONE); + + section("Type changes"); + code("var a return a = 12").equals("12"); + code("var a a = 12 return a").equals("12"); + code_v1("var a return a = 12.5").equals("12,5"); + code_v2_("var a return a = 12.5").equals("12.5"); + code_v1("var a a = 12.5 return a").equals("12,5"); + code_v2_("var a a = 12.5 return a").equals("12.5"); + code("var a return a = 'a'").equals("a"); + code("var a a = 'a' return a").equals("a"); + // code("var a return a = 12m").equals("12"); + // code("var a a = 12m return a").equals("12"); + code("var a = 2 return a = 'hello'").equals("hello"); + code("var a = 'hello' return a = 2").equals("2"); + code("var a = 2 a = 'hello' return a").equals("hello"); + code("var a = 2 a = [1, 2] return a").equals("[1, 2]"); + code_v2_("var a = 5.5 a = {} return a").equals("{}"); + // code("var a = [5, 7] a = 7 System.print(a)").output("7\n"); + // code("var a = 7 a = [5, 12] a").equals("[5, 12]"); + // code("var a = 7 System.print(a) a = <5, 12> System.print(a)").output("7\n<5, 12>\n"); + // code("var a = 5 a = 200l").equals("200"); + // code("var a = 5 a = 200l a").equals("200"); + // code("var a = 200l a = 5").equals("5"); + // code("var a = 200l a = 5 a").equals("5"); + // code("var a = 5.5 a = 200l a").equals("200"); + code("var a = 5.5 return a = 2").equals("2"); + // code("var a = 5.5 a = 1000m").equals("1000"); + // code("var a = 5.5 a = 2m ** 100").equals("1267650600228229401496703205376"); + // code("var a = 2m return a = 5").equals("5"); + // code("var a = 5.5 System.print(a) a = 2 System.print(a) a = 200l System.print(a) a = 1000m System.print(a) a = 'hello' System.print(a)").output("5.5\n2\n200\n1000\nhello\n"); + // code("var a = [] a = 5m").equals("5"); + + // section("Value.copy()"); + // code("2.copy()").equals("2"); + // code("2.5.copy()").equals("2.5"); + // code("12l.copy()").equals("12"); + // code("100m").equals("100"); + // code("'abc'.copy()").equals("'abc'"); + // code("[].copy()").equals("[]"); + // code("[1, 2, 3].copy()").equals("[1, 2, 3]"); + // code("[1.5, 2.5, 3.5].copy()").equals("[1.5, 2.5, 3.5]"); + // code("[1..1000].copy()").equals("[1..1000]"); + // code("[:].copy()").equals("[:]"); + // code("{}.copy()").equals("{}"); + // code("(x -> x).copy()").equals(""); + // code("Number.copy()").equals(""); + + section("Assignments"); + // code("var b = 0 { b = 12 } return b").equals("12"); + // code("var i = 12 { i = 'salut' } return i").equals("salut"); + // code("var i = 12 {{{ i = 'salut' }}} return i").equals("salut"); + code("var b = 5 if (1) { b = 'salut' } return b").equals("salut"); + code("var b = 5 if (0) { b = 'salut' } return b").equals("5"); + code("var a = 12 if (1) { a = 5 a++ } else { a = 3 } return a").equals("6"); + code_v1("var a = 12 if (0) { a = 5 a++ } else { a = 5.5 } return a").equals("5,5"); + code_v2_("var a = 12 if (0) { a = 5 a++ } else { a = 5.5 } return a").equals("5.5"); + // code("var a = 12 if (0) { a = 5 a++ } else { a = 7l } return a").equals("7"); + code("var b = 5 if (1) {} else { b = 'salut' } return b").equals("5"); + code("var b = 5 if (0) {} else { b = 'salut' } return b").equals("salut"); + code("var x = 5 if (true) if (true) x = 'a' return x").equals("a"); + code("var x = 5 if (true) if (true) if (true) if (true) if (true) x = 'a' return x").equals("a"); + // code("var x = 2 var y = { if (x == 0) { return 'error' } 7 * x } return y").equals("14"); + code("var y if (false) { if (true) {;} else { y = 2 } } else { y = 5 } return y").equals("5"); + code("PI = PI + 12; return PI").error(Error.CANT_ASSIGN_VALUE); + code_v1("var grow = []; var n = []; grow = @n; return grow").equals("[]"); + code("var PI = 3 return PI").equals("3"); + + section("Assignments with +="); + code_v1("var a = 10 a += 0.5 return a").equals("10,5"); + code_v2_("var a = 10 a += 0.5 return a").equals("10.5"); + + section("File"); + file("ai/code/trivial.leek").equals("2"); } } diff --git a/src/test/java/test/TestGlobals.java b/src/test/java/test/TestGlobals.java new file mode 100644 index 00000000..66b89feb --- /dev/null +++ b/src/test/java/test/TestGlobals.java @@ -0,0 +1,47 @@ +package test; + +public class TestGlobals extends TestCommon { + + public void run() throws Exception { + + section("Globals"); + code("global x; return x;").equals("null"); + code("global x = null; return x;").equals("null"); + code("global x = 12; return x;").equals("12"); + code("global x = [1, 2, 3]; return x;").equals("[1, 2, 3]"); + code("global x; x = 12 return x;").equals("12"); + code("global x; x = [1, 2, 3]; return x;").equals("[1, 2, 3]"); + code("var r = x; global x; return r;").equals("null"); + code("var r = x; global x = 12; return r;").equals("null"); + code("global r = 2 + 2; return r").equals("4"); + code("global r = [1, 2, 3]; return r").equals("[1, 2, 3]"); + code("global r = 'salut'; return r").equals("salut"); + code("global r = ['a': 12, 'b': 5]; return r").equals("[a : 12, b : 5]"); + code("global r = [] return r[1] = 12").equals("12"); + code("global r = [0] return r[0] += 12").equals("12"); + code("global r = [] return r[5] += 12").equals("12"); + code_v1("global r = 12 r = @null").equals("null"); + code("global m = [] return m = m").equals("[]"); + code_v2_("global m = {} return m = m").equals("{}"); + code_v2_("global m = {a: 12} return m = m").equals("{a: 12}"); + + section("Globals operators"); + code("global x = 12; x++; return x;").equals("13"); + code("global x = 12; x--; return x;").equals("11"); + code("global x = 12; ++x; return x;").equals("13"); + code("global x = 12; --x; return x;").equals("11"); + code("global x = 12; x += 5; return x;").equals("17"); + code("global x = 12; x -= 5; return x;").equals("7"); + code("global x = 12; x *= 5; return x;").equals("60"); + code_v1("global x = 12; x /= 5; return x;").equals("2,4"); + code_v2_("global x = 12; x /= 5; return x;").equals("2.4"); + code("global x = 12; x %= 5; return x;").equals("2"); + code("global x = 2; x **= 5; return x;").equals("32"); + code("global x = 12; x |= 5; return x;").equals("13"); + code("global x = 12; x &= 5; return x;").equals("4"); + code_v1("global x = 12; x ^= 5; return x;").equals("248832"); + code_v2_("global x = 12; x ^= 5; return x;").equals("9"); + code("global x = 12; return x == 5;").equals("false"); + code("global x = 12; return x === 5;").equals("false"); + } +} \ No newline at end of file diff --git a/src/test/java/test/TestIf.java b/src/test/java/test/TestIf.java new file mode 100644 index 00000000..d9807169 --- /dev/null +++ b/src/test/java/test/TestIf.java @@ -0,0 +1,95 @@ +package test; + +public class TestIf extends TestCommon { + + public void run() throws Exception { + /* + * Conditions + */ + header("Conditions"); + // code("if true then 12 else 5 end").equals("12"); + // code("if false then 12 else 5 end").equals("5"); + code("if (true) { return 12 } else { return 5 }").equals("12"); + code("if (false) { return 12 } else { return 5 }").equals("5"); + // code("let a = if (false) { 12 } else { 5 } a").equals("5"); + // code("let a = if (true) { 'yo' } else { 'hello' } a").equals("'yo'"); + // code("let a = if (true) { 12 } else { 'hello' } a").equals("12"); + // code("let a = if (true) { 'hello' } else { 12 } a").equals("'hello'"); + // code("if (true) {} else {}").equals("{}"); + code("if (true) {;} else {}").equals("null"); + // code("if (true) { {} } else {}").equals("{}"); + code("if (true) null else {}").equals("null"); + // code("if true").error(ls::Error::UNEXPECTED_TOKEN, {""}); + // code("if true else").error(ls::Error::UNEXPECTED_TOKEN, {"else"}); + code_v2_("if (true) { return {a: 12} } else { return {b: 5} }").equals("{a: 12}"); + code("if (true) return 12 else return 5;").equals("12"); + code("if (false) return 12 else return 5;").equals("5"); + code("if (true) return 12;").equals("12"); + code("if (false) return 12;").equals("null"); + // code("if true then 12 end").equals("12"); + // code("if false then 12 end").equals("null"); + // code("if (true) { 5 } else { return 2 }").equals("5"); + code("if (true) { return 5 } else { 2 }").equals("5"); + code("if (false) { 5 } else { return 2 }").equals("2"); + // code("if (false) { return 5 } else { 2 }").equals("2"); + // code("let a = 5m if true { a } else { 2m }").equals("5"); + // code("let a = 5m if true { a } else { a }").equals("5"); + // code("if true then 1 else 2 end").equals("1"); + // code("if true then if false then 1 else 2 end end").equals("2"); + // code("if true then if false then 1 end else 2 end").equals("null"); + code("if (false) { return 12 } return 5;").equals("5"); + code("var k = '121212' if (false) { return 12 } return 5;").equals("5"); + code("var L = 5 if (L < 1) {;}").equals("null"); + code("var L = 5 if (L > 1) {;}").equals("null"); + code("if (false) { return 'hello' }").equals("null"); + // code("let x = { if 1 2 else 3 } let y = { if x == 0 { 'error' } else { 8 * x } } y").equals("16"); + code("var test = 0; if(false) if(true) test = 3; else test = 1; return test;").equals("0"); + + code("var a = 1; if(a is 1) return 2; else return 0").equals("2"); + code("var a = 1; if(a is 2) return 2; else return 0").equals("0"); + code("var a = 1; if(a is not 2) return 2; else return 0").equals("2"); + code("var a = 1; if(a is not 1) return 2; else return 0").equals("0"); + code("var a = true; if(not a) return 2; else return 0").equals("0"); + code("var Bob = 12 if (Bob = 75);").equals("null"); + code("var cell = 1 if (cell != null) return 12").equals("12"); + code("var cell = null if (cell != null) return 12 return 5").equals("5"); + code("function t(c) { var cell = c if (cell!=null ) 1; } return t(300);").equals("null"); + code_v1("function t(@c) { var cell = c if (cell!=null ) 1; } return t(300);").equals("null"); + code_v1("function t(@c) { var cell = c } for (var i = 0; i < 10; ++i) return t(i);").equals("null"); + code_v1("function t(@c) { var cell = c if (cell!=null ) 1; } for (var i = 0; i < 10; ++i) return t(i);").equals("null"); + code_v1("function t(@c) { var cell = c cell != null } for (var i = 0; i < 10; ++i) return t(i);").equals("null"); + code_v1("function t(@c) { var cell = c return cell != null } for (var i = 0; i < 10; ++i) return t(i);").equals("true"); + + section("Conditions with other types"); + code("if (1212) { return 'ok' } else { return 5 }").equals("ok"); + code("if (['str', true][0]) { return 12 } else { return 5 }").equals("12"); + code("if (null) { return 12 } else { return 5 }").equals("5"); + + section("Different branch types"); + code("if (1) return ['a'] else if (0) return [2] else return [5.5];").equals("[a]"); + code("if (0) return ['a'] else if (1) return [2] else return [5.5];").equals("[2]"); + code_v1("if (0) return ['a'] else if (0) return [2] else return [5.5];").equals("[5,5]"); + code_v2_("if (0) return ['a'] else if (0) return [2] else return [5.5];").equals("[5.5]"); + + section("Ternary conditions"); + code("return true ? 5 : 12;").equals("5"); + code("return false ? 5 : 12;").equals("12"); + code("return true ? 'a' : 'b';").equals("a"); + code("return false ? 'a' : 'b';").equals("b"); + code("return true ? 'a' : 5;").equals("a"); + code("return false ? 'a' : 5;").equals("5"); + code("return true ? 5 : 'b';").equals("5"); + code("return false ? 5 : 'b';").equals("b"); + code("return 'good' ? 5 : 12;").equals("5"); + code("return '' ? 5 : 12;").equals("12"); + code("return 'good' ? 'a' : 'b';").equals("a"); + code("return true ? true ? 5 : 12 : 7;").equals("5"); + code("return true ? false ? 5 : 12 : 7;").equals("12"); + code("return false ? false ? 5 : 12 : 7;").equals("7"); + code("return false ? true ? 5 : 12 : 7;").equals("7"); + code("return true ? true ? true ? 5 : 12 : 7 : 8;").equals("5"); + code("return true ? true ? false ? 5 : 12 : 7 : 8;").equals("12"); + code("return true ? false ? false ? 5 : 12 : 7 : 8;").equals("7"); + code("return (5 > 10) ? 'a' : (4 == 2 ** 2) ? 'yes' : 'no';").equals("yes"); + } +} \ No newline at end of file diff --git a/src/test/java/test/TestJSON.java b/src/test/java/test/TestJSON.java new file mode 100644 index 00000000..d165be15 --- /dev/null +++ b/src/test/java/test/TestJSON.java @@ -0,0 +1,113 @@ +package test; + +public class TestJSON extends TestCommon { + + public void run() throws Exception { + + section("jsonEncode()"); + // code("jsonEncode()").error(ls::Error::Type::WRONG_ARGUMENT_COUNT, {"jsonEncode", "1", "0"}); + // integer + code("return jsonEncode(0)").equals("0"); + code("return jsonEncode(12)").equals("12"); + code("return jsonEncode(-589)").equals("-589"); + // real + code("return jsonEncode(54.123)").equals("54.123"); + code("return jsonEncode(-65.89)").equals("-65.89"); + // long + // code("return jsonEncode(1l)").equals("1"); + // code("return jsonEncode(1234567890987)").equals("1234567890987"); + // mpz + // code("return jsonEncode(1m)").equals("1"); + // code("return jsonEncode(123456789098712345678909871234567890987m)").equals("'123456789098712345678909871234567890987'"); + // code("return jsonEncode(15m ** 5)").equals("'759375'"); + // boolean + code("return jsonEncode(true)").equals("true"); + code("return jsonEncode(false)").equals("false"); + code("return jsonEncode(12 > 5)").equals("true"); + // string + code("return jsonEncode('')").equals("\"\""); + code("return jsonEncode('hello')").equals("\"hello\""); + // array + code("return jsonEncode([])").equals("[]"); + code("return jsonEncode([1, 2, 3])").equals("[1,2,3]"); + // object + // code("return jsonEncode({})").equals("'{}'"); + // code("return jsonEncode({a: 1, b: 2, c: 3})").equals("'{\"a\":1,\"b\":2,\"c\":3}'"); + // code("return jsonEncode({hello: [], b: {d: 12}, ccccc: [1, 2, [], 4]})").equals("'{\"b\":{\"d\":12},\"ccccc\":[1, 2, [], 4],\"hello\":[]}'"); + // function : not transformable into JSON + // code("return jsonEncode(x -> x)").equals("''"); + // code("return jsonEncode([1, x -> x, 3])").equals("'[1, 3]'"); + + // section("Value.json()"); + // // null + // code("null.json()").equals("'null'"); + // // integer + // code("0.json()").equals("'0'"); + // code("12.json()").equals("'12'"); + // code("(-589).json()").equals("'-589'"); + // // real + // code("54.123.json()").equals("'54.123'"); + // code("(-65.89).json()").equals("'-65.89'"); + // // long + // code("(1l).json()").equals("'1'"); + // code("1234567890987.json()").equals("'1234567890987'"); + // // boolean + // code("true.json()").equals("'true'"); + // code("false.json()").equals("'false'"); + // code("(12 > 5).json()").equals("'true'"); + // // string + // code("''.json()").equals("'\"\"'"); + // code("'hello'.json()").equals("'\"hello\"'"); + // // array + // code("[].json()").equals("'[]'"); + // code("[1, 2, 3].json()").equals("'[1, 2, 3]'"); + // code("['a', 'b', 'c'].json()").equals("'[\"a\", \"b\", \"c\"]'"); + // // set + // code("<1, 2, 3>.json()").equals("'[1, 2, 3]'"); + // code("<9.99>.json()").equals("'[9.990000]'"); + // code("<'a', 'b', 'c'>.json()").equals("'[\"a\", \"b\", \"c\"]'"); + // // map + // code("[1: 1].json()").equals("'{\"1\": 1}'"); + // code("['1': 1].json()").equals("'{\"1\": 1}'"); + // code("['a': 'b'].json()").equals("'{\"a\": \"b\"}'"); + // // object + // code("{}.json()").equals("'{}'"); + // code("{a: 1, b: 2, c: 3}.json()").equals("'{\"a\":1,\"b\":2,\"c\":3}'"); + // code("{hello: [], b: {d: 12}, ccccc: [1, 2, [], 4]}.json()").equals("'{\"b\":{\"d\":12},\"ccccc\":[1, 2, [], 4],\"hello\":[]}'"); + // // class + // code("Number.json()").equals("'\"\"'"); + + section("jsonDecode()"); + code("return jsonDecode('')").equals("null"); + code("return jsonDecode('null')").equals("null"); + code("return jsonDecode('true')").equals("true"); + code("return jsonDecode('false')").equals("false"); + + code("return jsonDecode('12')").equals("12"); + code("return jsonDecode('-589')").equals("-589"); + code_v1("return jsonDecode('54.123')").equals("54,123"); + code_v2_("return jsonDecode('54.123')").equals("54.123"); + code_v1("return jsonDecode('-65.89')").equals("-65,89"); + code_v2_("return jsonDecode('-65.89')").equals("-65.89"); + // code("return jsonDecode('1234567890987')").equals("1234567890987"); + + code("return jsonDecode('\"\"')").equals(""); + code("return jsonDecode('\"hello\"')").equals("hello"); + + code("return jsonDecode('[]')").equals("[]"); + code("return jsonDecode('[1,2,3]')").equals("[1, 2, 3]"); + code_v1("return jsonDecode('[1.6,2.1,3.77]')").equals("[1,6, 2,1, 3,77]"); + code_v2_("return jsonDecode('[1.6,2.1,3.77]')").equals("[1.6, 2.1, 3.77]"); + code("return jsonDecode('[\"a\",\"b\",\"c\"]')").equals("[a, b, c]"); + code("return jsonDecode('[[],[[],[]],[]]')").equals("[[], [[], []], []]"); + + // code("return jsonDecode('{}')").equals("{}"); + // code("return jsonDecode('{\"a\":1,\"b\":2,\"c\":3}')").equals("{a: 1, b: 2, c: 3}"); + // code("return jsonDecode('{\"b\":{\"d\":12},\"ccccc\":[1,2,[],4],\"hello\":[]}')").equals("{b: {d: 12}, ccccc: [1, 2, [], 4], hello: []}"); + + section("Combinations"); + code("var v = 'salut' return jsonDecode(jsonEncode(v)) == v").equals("true"); + // code("var v = {b: {d: 12}, cc: [[], 4], h: []} return jsonDecode(jsonEncode(v)) == v").equals("true"); + code("var v = 'salut' return jsonEncode(jsonEncode(v))").equals("\"\\\"salut\\\"\""); + } +} diff --git a/src/test/java/test/TestLoops.java b/src/test/java/test/TestLoops.java new file mode 100644 index 00000000..17fbc8a8 --- /dev/null +++ b/src/test/java/test/TestLoops.java @@ -0,0 +1,239 @@ +package test; + +import leekscript.common.Error; + +public class TestLoops extends TestCommon { + + public void run() { + + /* + * While loops + */ + section("While loops"); + code("var i = 0 while (i < 10) { i++ } return i;").equals("10"); + code("var i = 0 var s = 0 while (i < 10) { s += i i++ } return s;").equals("45"); + code("var i = 0 while (i < 100) { i++ if (i == 50) break } return i;").equals("50"); + code("var i = 0 var a = 0 while (i < 10) { i++ if (i < 8) continue a++ } return a;").equals("3"); + code("while (true) { break }").equals("null"); + code("var i = 10 while (['hello', i][1]) { i-- } return i;").equals("0"); + code("var i = 0 while (i < 10) i++ return i;").equals("10"); + // code("var i = 5 while (i-- > 0) { System.print(i) }").output("4\n3\n2\n1\n0\n"); + code("while (true) { return 12 }").equals("12"); + code("var n = 5 var a = [] while (n--) { push(a, 1) }").equals("null"); + code("var n = 5 var a = [] while (n--) { push(a, 1) } return a;").equals("[1, 1, 1, 1, 1]"); + code("var n = 5 var a = [] while (n--) { push(a, 1) } return a;").equals("[1, 1, 1, 1, 1]"); + code("var mp = 10, grow = [100] while (mp--) { grow = [1] } return grow;").equals("[1]"); + // DISABLED_code("var a = [] while |a += 'a'| < 5 { a += 'b' } return a;").equals("['a', 'b', 'a', 'b', 'a']"); + code("var s = 0 var j = [] while (count(j) < 10) { push(j, 'a') s++ } return s;").equals("10"); + // DISABLED_code("var s = [] var i = 10 while (i--) { if i < 5 { s += i } else { s += ('a'.code() + i).char() } } return s;").equals("['j', 'i', 'h', 'g', 'f', 4, 3, 2, 1, 0]"); + // DISABLED_code("var s = <> var i = 10 while (i--) { if i < 5 { s += i } else { s += ('a'.code() + i).char() } } return s;").equals("<0, 1, 2, 3, 4, 'f', 'g', 'h', 'i', 'j'>"); + // DISABLED_code("var s = [:] var i = 10 while (i--) { if i < 5 { s[i] = 5.5 } else { s['a'] = i } } return s;").equals("[0: 5.5, 1: 5.5, 2: 5.5, 3: 5.5, 4: 5.5, 'a': 5]"); + // DISABLED_code("var s = [:] var i = 10 while (i--) { if i < 5 { s[i] = 12 } else { s[1 / i] = 7 } } return s;").equals("[0: 12, 0.111111: 7, 0.125: 7, 0.142857: 7, 0.166667: 7, 0.2: 7, 1: 12, 2: 12, 3: 12, 4: 12]"); + // DISABLED_code("var s = [:] var i = 10 while (i--) { if i < 5 { s[i] = 12 } else { s[12] = i } } return s;").equals("[0: 12, 1: 12, 2: 12, 3: 12, 4: 12, 12: 5]"); + // DISABLED_code("var a = 5 while (a-- > 0);").equals("null"); + code("var i = 10 while (i--); return i;").equals("-1"); + code("var i = 10 while (--i); return i;").equals("0"); + code("var t = 0; while(t<5){ t++; return t;}").equals("1"); + code("var t = 0; while(t<5){ t++; return t;} return 0;").equals("1"); + + section("Double while loops"); + code("var s = [] var i = 0 while (i < 2) { i++ var j = 0 while (j < 3) { j++ push(s, j) }} return s;").equals("[1, 2, 3, 1, 2, 3]"); + code("var s = [] var i = 0 var j = 0 while (i < 2) { i++ j = 0 while (j < 3) { j++ push(s, j) }} return s;").equals("[1, 2, 3, 1, 2, 3]"); + code("var s = [] var i = 0 var j = 0 while (i < 4) { j = i i++ while (j < 4) { j++ push(s, j) }} return s;").equals("[1, 2, 3, 4, 2, 3, 4, 3, 4, 4]"); + code("var s = [] var i = 0 while (i < 2) { i++ var j = 0 while (j < 2) { j++ var k = 0 while (k < 2) { k++ push(s, k) }}} return s;").equals("[1, 2, 1, 2, 1, 2, 1, 2]"); + code_v1("var s = [] var i = 0 while (i < 2) { i++ push(s, 0.5) var j = 0 while (j < 3) { j++ push(s, j) }} return s;").equals("[0,5, 1, 2, 3, 0,5, 1, 2, 3]"); + code_v2_("var s = [] var i = 0 while (i < 2) { i++ push(s, 0.5) var j = 0 while (j < 3) { j++ push(s, j) }} return s;").equals("[0.5, 1, 2, 3, 0.5, 1, 2, 3]"); + // DISABLED_code("var s = [] var i = 0 while (i < 2) { i++ s.push([]) var j = 0 while j < 3 { j++ s[|s| - 1] += 1 }} s").equals("[[1, 1, 1], [1, 1, 1]]"); + // DISABLED_code("var s = [] var i = 0 while (i < 2) { i++ s.push([]) var j = 0 while j < 3 { j++ s[|s| - 1] += ('a'.code() + 3 * (i - 1) + j - 1).char() }} s").equals("[['a', 'b', 'c'], ['d', 'e', 'f']]"); + // file("test/code/loops/lot_of_whiles_int.leek").equals("30030"); + // file("test/code/loops/lot_of_whiles_array.leek").equals("30030"); + + section("Do while"); + code("var i = 0 do { i++ } while (i < 10); return i;").equals("10"); + code("var i = 0 var s = 0 do { s += i i++ } while (i < 10); return s;").equals("45"); + code("var i = 0 do { i++ if (i == 50) break } while (i < 100); return i;").equals("50"); + code("function f(A) { var i = 0 do { i++ } while (i < A); return i } return f(10);").equals("10"); + code("var i = 0 var a = 0 do { i++ if (i < 8) continue a++ } while (i < 10); return a;").equals("3"); + code("do { break } while (true);").equals("null"); + code("do { if (true) return 12 } while (true); return 1;").equals("12"); + code("var t = 0; do { t++; return t; } while (t < 5);").equals("1"); + // TODO catch error + // code("var t = 0; do { t++; return t;} while (t < 5); return 2;").equals("1"); + + // header("For loops"); + // code("for var i = 0; ; i++ {}").ops_limit(1000).exception(ls::vm::Exception::OPERATION_LIMIT_EXCEEDED); + code("for (var i = 0; false; i++) {}").equals("null"); + code("for (var i = 0; i < 10; i++) {}").equals("null"); + // DISABLED_code("var s = 0 for (var i = 0; i < 5; i++) do s += i end s").equals("10"); + // DISABLED_code("var s = 0 for (var i = 0; i < 10; i += 2) do s += i end s").equals("20"); + code("var s = 0 for (var i = 0; i < 5; i++) { s += i } return s;").equals("10"); + code("var s = 0 for (var i = 0; i < 10; i += 2) { s += i } return s;").equals("20"); + code("var a = 0 for (var i = 0; i < 10; i++) { a++ } return a;").equals("10"); + code("var a = 0 for (var i = 0; i < 10; i++) { if (i < 5) { continue } a++ } return a;").equals("5"); + code("var a = 0 for (var i = 0; i < 10; i++) { if (i > 5) { break } a++ } return a;").equals("6"); + code("var c = 0 for (var t = []; count(t) < 10; push(t, 'x')) { c++ } return c;").equals("10"); + // DISABLED_code("var s = 0 for (var m = [1: 3, 2: 2, 3: 1]; m; var l = 0 for (k, x in m) { l = k } m.erase(l)) { for (x in m) { s += x } } return s;").equals("14"); + code("for (var i = 0; ['', i < 10][1]; i++) {}").equals("null"); + // DISABLED_code("var i = ['', 1][1] for (; i < 10; i <<= 1) {}").equals("null"); + // code("for (var i = 0, j = 0; i < 5; i++, j++) { System.print(i + ', ' + j) }").output("0, 0\n1, 1\n2, 2\n3, 3\n4, 4\n"); + // code("for (var i = 0, j = 10; i < 5; i++, j += 2) { System.print(i + ', ' + j) }").output("0, 10\n1, 12\n2, 14\n3, 16\n4, 18\n"); + // DISABLED_code("for (var i = 0, j = 1, k = 2, l = 3; i < 5; i++, j++, k++, l++) { System.print([i j k l]) }").output("[0, 1, 2, 3]\n[1, 2, 3, 4]\n[2, 3, 4, 5]\n[3, 4, 5, 6]\n[4, 5, 6, 7]\n"); + // code("for var i = 0m; i < 10m; i++ {}").equals("(void)"); + // code("var s = 0m for var i = 0m; i < 10m; i++ { s += i } s").equals("45"); + // code("var s = 0m for var i = 0m; i < 10m; i += 2m { s += i } s").equals("20"); + + section("For variable defined before the loop"); + // DISABLED_code("var i = 0 for (; i < 10; i++) { } return i;").equals("10"); + code("var i = 0 for (i = 0; i < 10; i++) { } return i;").equals("10"); + code("var i = 0 for (i = 0; i < 10; i++) { if (i == 5) { break } } return i;").equals("5"); + code_v1("var i var c = 0 for (i = 0; i < 20; i += 0.573) { c++ } return i;").equals("20,055"); + code_v2_("var i var c = 0 for (i = 0; i < 20; i += 0.573) { c++ } return i;").equals("20.05500000000001"); + code("var i = 's' var c = 0 for (i = []; count(i) < 8; push(i, 1)) { c++ } return i;").equals("[1, 1, 1, 1, 1, 1, 1, 1]"); + // DISABLED_code("var i = 0 for (; i < 10; i += 0.5) { } return i;").equals("10"); + // code("var i = 0 for (i = 2l; i < 10; i += 0.5) { } return i;").equals("10"); + + section("For whitout braces"); + code("var s = 0 for (var i = 0; i < 10; i++) s += i return s;").equals("45"); + + section("For loops with returns"); + // DISABLED_code("for (return 12; true; null) {}").equals("12"); + // DISABLED_code("for (;; return 'hello') {}").equals("'hello'"); + code("for(var i=0;i<3;i++){ return i; }").equals("0"); + code("for(var i=0;i<3;i++){ return i; } return 2;").equals("0"); + + section("Nested for loops"); + code("var s = 0 for (var i = 0; i < 10; ++i) { for (var j = 0; j < 10; ++j) { s++ }} return s;").equals("100"); + code("var s = 0 for (var i = 0; i < 5; ++i) { for (var j = 0; j < 5; ++j) { for (var k = 0; k < 5; ++k) { s++ }}} return s;").equals("125"); + code("var s = 0 for (var i = 0; i < 10; i += 1) { for (var j = 0; j < 10; j += 1) { s++ }} return s;").equals("100"); + // DISABLED_code("var s = 0 for (var i = 0; i < 10; i += 1) { var j = 0 for (; j < 10; j += 1) { s++ }} return s;").equals("100"); + // file("test/code/loops/lot_of_fors_int.leek").equals("15015"); + // file("test/code/loops/lot_of_fors_array.leek").equals("15015"); + code("var tabmulti=[]; for (var i = 0; i < 8; ++i) tabmulti[i]=1; return tabmulti").equals("[1, 1, 1, 1, 1, 1, 1, 1]"); + code("var tabmulti=[]; for (var i = 0; i < 9; ++i) tabmulti[i]=1; return tabmulti").equals("[1, 1, 1, 1, 1, 1, 1, 1, 1]"); + code("var tabmulti=[]; for (var i = 0; i < 50; ++i) tabmulti[i]=1; return tabmulti").equals("[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]"); + code("var tabmulti=[[],[],[],[],[]]; var i = 3, j = -2 tabmulti[i][j]=i*j; return tabmulti").equals("[[], [], [], [-2 : -6], []]"); + code("var tabmulti=[[],[],[],[],[]]; var vPM=4; for(var i=0;i<=vPM;i++){ for(var j=-vPM;j<=vPM;j++){ }} return tabmulti").equals("[[], [], [], [], []]"); + code("var tabmulti=[[],[],[],[],[]]; var vPM=1; for(var i=0;i<=vPM;i++){ for(var j=-vPM;j<=vPM;j++){ tabmulti[i][j]=i*j;}} return tabmulti").equals("[[-1 : 0, 0 : 0, 1 : 0], [-1 : -1, 0 : 0, 1 : 1], [], [], []]"); + code("var tabmulti=[[],[],[],[],[]]; var vPM=2; for(var i=0;i<=vPM;i++){ for(var j=-vPM;j<=vPM;j++){ tabmulti[i][j]=i*j;}} return tabmulti").equals("[[-2 : 0, -1 : 0, 0 : 0, 1 : 0, 2 : 0], [-2 : -2, -1 : -1, 0 : 0, 1 : 1, 2 : 2], [-2 : -4, -1 : -2, 0 : 0, 1 : 2, 2 : 4], [], []]"); + code("var tabmulti=[[],[],[],[],[]]; var vPM=3; for(var i=0;i<=vPM;i++){ for(var j=-vPM;j<=vPM;j++){ tabmulti[i][j]=i*j;}} return tabmulti").equals("[[-3 : 0, -2 : 0, -1 : 0, 0 : 0, 1 : 0, 2 : 0, 3 : 0], [-3 : -3, -2 : -2, -1 : -1, 0 : 0, 1 : 1, 2 : 2, 3 : 3], [-3 : -6, -2 : -4, -1 : -2, 0 : 0, 1 : 2, 2 : 4, 3 : 6], [-3 : -9, -2 : -6, -1 : -3, 0 : 0, 1 : 3, 2 : 6, 3 : 9], []]"); + code("var tabmulti=[[],[],[],[],[]]; var vPM=4; for(var i=0;i<=vPM;i++){ for(var j=-vPM;j<=vPM;j++){ tabmulti[i][j]=i*j;}} return tabmulti").equals("[[-4 : 0, -3 : 0, -2 : 0, -1 : 0, 0 : 0, 1 : 0, 2 : 0, 3 : 0, 4 : 0], [-4 : -4, -3 : -3, -2 : -2, -1 : -1, 0 : 0, 1 : 1, 2 : 2, 3 : 3, 4 : 4], [-4 : -8, -3 : -6, -2 : -4, -1 : -2, 0 : 0, 1 : 2, 2 : 4, 3 : 6, 4 : 8], [-4 : -12, -3 : -9, -2 : -6, -1 : -3, 0 : 0, 1 : 3, 2 : 6, 3 : 9, 4 : 12], [-4 : -16, -3 : -12, -2 : -8, -1 : -4, 0 : 0, 1 : 4, 2 : 8, 3 : 12, 4 : 16]]"); + + section("Mix for and while loops"); + code("var s = 0 for (var i = 0; i < 10; i += 1) { var j = 10 while (j--) { s++ }} return s;").equals("100"); + code("var s = [] for (var i = 0; i < 2; i += 1) { var j = 0 while (j < 3) { j++ push(s, 1) }} return s;").equals("[1, 1, 1, 1, 1, 1]"); + code("var s = 0 for (var i = 0; i < 10; i += 1) { var j = [] while (count(j) < 10) { push(j, 'a') s++ }} return s;").equals("100"); + code("var s = [] var i = 3 while (i--) { for (var j = 0; j < 2; ++j) { push(s, j) }} return s;").equals("[0, 1, 0, 1, 0, 1]"); + code("var s = [] var i = 3 var j while (i--) { for (j = 0; j < 2; ++j) { push(s, j) }} return s;").equals("[0, 1, 0, 1, 0, 1]"); + code("for (var x in [1, 2, 3]) { for (var x = 0; x < 10; ++x) { } }").error(Error.VARIABLE_NAME_UNAVAILABLE); + + header("Foreach loops"); + section("Empty containers"); + code("for (var v in []) {}").equals("null"); + // code("for (v in new Array) {}").equals("(void)"); + code("var s = 0; var a; for (a in [1, 2, 3]) { s += a } return s").equals("6"); + code("var s = 0; var a; for (var k : a in [1, 2, 3]) { s += a } return s").equals("6"); + code("var s = 0; var k; for (k : var a in [1, 2, 3, 4, 5]) { s += k } return s").equals("10"); + DISABLED_code("for (var x in 12) {}").error(Error.NOT_ITERABLE); + + section("Normal containers"); + code("for (var v in [1, 2, 3, 4]) {}").equals("null"); + // code("for (var v in [1, 2, 3, 4]) do end").equals("(void)"); + code("var s = 0 for (var v in [1, 2, 3, 4]) { s += v } return s;").equals("10"); + // code("var s = 0 for v in [1l, 2l, 3l, 4l] { s += v } return s;").equals("10"); + // code("var s = 0.0 for v in [1.2, 2, 3.76, 4.01] { s += v } s").almost(10.97); + // code("var s = 0 for v in [1.2, 2, 3.76, 4.01] { s += v } s").almost(10.97); + code("var s = '' for (var v in ['salut ', 'ça ', 'va ?']) { s += v } return s;").equals("salut ça va ?"); + code("var a = 0 var x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] for (var i in x) { if (i < 5) { continue } a++ } return a;").equals("5"); + code("var s = 0 for (var k : var v in [1, 2, 3, 4]) { s += k * v } return s;").equals("20"); + // code("var s = '' for (var k : var v in ['a': 1, 'b': 2, 'c': 3, 'd': 4]) { s += v * k } return s;").equals("abbcccdddd"); + code_v1("return (function (a) { var s = 0.0; for (var x in a) { s += x } return s; })([1, 2, 3, 4.25]);").equals("10,25"); + code_v2_("return (function (a) { var s = 0.0; for (var x in a) { s += x } return s; })([1, 2, 3, 4.25]);").equals("10.25"); + // DISABLED_code("var y = '' for k, x in { var x = [] x.push(4) x } { y += k + ':' + x + ' ' } y").equals("'0:4 '"); + // DISABLED_code("var y = '' for k, x in { var x = [1: 2] x.insert(3, 4) x } { y += k + ':' + x + ' ' } y").equals("'1:2 3:4 '"); + // DISABLED_code("var y = '' for k, x in { var x = [1: 2.5] x.insert(3, 4) x } { y += k + ':' + x + ' ' } y").equals("'1:2.5 3:4 '"); + // DISABLED_code("var y = '' for k, x in { var x = [1: '2'] x.insert(3, 4) x } { y += k + ':' + x + ' ' } y").equals("'1:2 3:4 '"); + // code("var y = 'test' for (var x in 1) { y = x } y").equals("1"); + // code("var y = 'test' for (var x in 'salut') { y = x } y").equals("'t'"); + code("var x = 'test' for (var x in [1]) {} return x;").error(Error.VARIABLE_NAME_UNAVAILABLE); + // code("var y = '' for k, x in { var x = <> x.insert(4) x } { y += k + ':' + x } y").equals("'0:4'"); + // DISABLED_code("var fs = [] fs.push(s -> {var sum = 0 for v in s {sum += v} sum}) fs[0](<1, 2>)").equals("3"); // TODO issue #243 + // DISABLED_code("var fs = [] fs.push(s -> {[for v in s {v}]}) fs[0](<2,1>)").equals("[1, 2]"); // TODO issue #243 + // code("var s = 0l for i in [0..1000] { s += i ** 2 } s").equals("333833500"); + // code("var S = 'salut' var N = [] for var s in S.split('') { N += String.code(s) } N").equals("[115, 97, 108, 117, 116]"); + code("var tab = [0,1,2,3]; for(var i in tab){ return i; }").equals("0"); + code("var tab = [0,1,2,3]; for(var i in tab){ return i; } return 5;").equals("0"); + code("var tab = [1:0,2:1,3:2,4:3]; for(var i : var j in tab){ return i; } ").equals("1"); + code("var tab = [1:0,2:1,3:2,4:3]; for(var i : var j in tab){ return i; } return 0;").equals("1"); + + section("Foreach - no braces"); + code("var s = 0 for (var v in [1, 2, 3, 4]) s += v return s;").equals("10"); + + section("Foreach - double"); + code("var r = [] for (var x in [1, 2, 3]) { for (var y in [4, 5, 6]) { push(r, x * y) }} return r;").equals("[4, 5, 6, 8, 10, 12, 12, 15, 18]"); + // code("var r = [] for x in ['a', 'b', 'c'] { for y in [4, 5, 6] { r += x * y }} r").equals("['aaaa', 'aaaaa', 'aaaaaa', 'bbbb', 'bbbbb', 'bbbbbb', 'cccc', 'ccccc', 'cccccc']"); + code("for (var x in [1, 2, 3]) { for (var x in [4, 5, 6]) { } }").error(Error.VARIABLE_NAME_UNAVAILABLE); + code("for (var x : var y1 in [1, 2, 3]) { for (var x : var y2 in [4, 5, 6]) { } }").error(Error.VARIABLE_NAME_UNAVAILABLE); + code("for (var x1 : var y in [1, 2, 3]) { for (var x2 : var y in [4, 5, 6]) { } }").error(Error.VARIABLE_NAME_UNAVAILABLE); + + section("Foreach - mix"); + // code("var n = 3 while (n--) { var r = [] for x in [1, 2, 3] { r += x } print(r) }").output("[1, 2, 3]\n[1, 2, 3]\n[1, 2, 3]\n"); + + section("Foreach - references"); + code_v1("var s = 0 for (var @v in [1, 2, 3, 4]) { s += v } return s;").equals("10"); + code_v1("var s = 0 for (var @k : var @v in [1, 2, 3, 4]) { s += k * v } return s;").equals("20"); + + section("Foreach - captures"); + code("for (var e in [1, 2, 3]) { (function() { return e })() } return null;").equals("null"); + code("for (var i : var e in [1, 2, 3]) { (function() { return e })() } return null;").equals("null"); + code_v1("var a = [1, 2, 3] for (var @e in a) { (function() { e = 5 })() } return a;").equals("[1, 2, 3]"); + code_v1("var a = [1, 2, 3] for (var i : var @e in a) { (function() { e = 5 })() } return a;").equals("[1, 2, 3]"); + code("var a = [1, 2, 3] for (var i : var v in a) { (function() { i += 1 })() } return a;").equals("[1, 2, 3]"); + + section("Foreach - return"); + code("for (var x in [1]) { return 12 }").equals("12"); + // code("for (var x in 'salut') { return 13 }").equals("13"); + // code("for (var x in 123) { return 14 }").equals("14"); + + section("Foreach - argument"); + code("function main(r) { for (var x in [1, 2, 3]) { for (var y in [4, 5, 6]) { push(r, x * y) }} return r } return main([]);").equals("[4, 5, 6, 8, 10, 12, 12, 15, 18]"); + + // header("Foreach - unknown container"); + // code("for x in ['hello', 12345][0] { print(x) }").equals("h\ne\nl\nl\no\n"); + + // header("Foreach - not iterable"); + // code("for x in null {}").error(ls::Error::Type::VALUE_NOT_ITERABLE, {"null", env.null->to_string()}); + // code("for x in true {}").error(ls::Error::Type::VALUE_NOT_ITERABLE, {"true", env.boolean->to_string()}); + // code("for x in Number {}").error(ls::Error::Type::VALUE_NOT_ITERABLE, {"Number", env.const_class()->to_string()}); + + // header("Array For"); + // code("[for var i = 0; i < 5; ++i { i }]").equals("[0, 1, 2, 3, 4]"); + // code("[for var i = 1; i <= 10; ++i { [for var j = 1; j <= 3; ++j { if i == 3 break 2 i * j}] }]").equals("[[1, 2, 3], [2, 4, 6]]"); + // code("[for x in [1, 2, 3] { x }]").equals("[1, 2, 3]"); + // code("let a = ['a': 'b', 'c': 'd'] [for k, x in a { k + x }]").equals("['ab', 'cd']"); + // code("[for x in [1, 2, 3] {[ for y in [1, 2, 3] { if y == 2 continue x * y }] }]").equals("[[1, 3], [2, 6], [3, 9]]"); + // code("let sorted = [for x in <5, 2, 4, 1, 3> { x }] sorted").equals("[1, 2, 3, 4, 5]"); + // code("[for i in [1..10] { i }]").equals("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"); + // DISABLED_code("function attrs(o) { [for k : v in o {v}] } attrs(['a'])").equals("['a']"); + // DISABLED_code("function attrs(o) { [for k : v in o {v}] } attrs([1])").equals("[1]"); + // DISABLED_code("function attrs(o) { [for k : v in o {v}] } attrs([])").equals("[]"); + // DISABLED_code("function f() { [for x in [1, 2, 3] { x }] } f()").equals("[1, 2, 3]"); + // DISABLED_code("function f() { for x in [1, 2, 3] { print(x) } } f()").output("1\n2\n3\n"); + + // header("Breaks and Continues"); + // code("break").error(ls::Error::Type::BREAK_MUST_BE_IN_LOOP, {}); + // code("continue").error(ls::Error::Type::CONTINUE_MUST_BE_IN_LOOP, {}); + // code("while (true) { break 2 }").error(ls::Error::Type::BREAK_MUST_BE_IN_LOOP, {}); + // code("while (true) { continue 2 }").error(ls::Error::Type::CONTINUE_MUST_BE_IN_LOOP, {}); + // code("var r = 0 for (var x in [1, 2]) { for (var y in [3, 4]) { r = 10 * x + y if (x + y) >= 5 break 2 }} r").equals("14"); + // code("var r = 0 for (var x in [1, 2]) { for (var y in [3, 4]) { r = 10 * x + y continue 2 } r = 0 } r").equals("23"); + // code("for (var x in ['a']) { var a = 'a' { var b = 'b' break } var d = 'd' } return 0;").equals("0"); + code("for (var x in ['a']) { var a = 'a' for (var y in ['a']) { var b = 'b' break } var d = 'd' } return 0;").equals("0"); + // code("for (var x in ['a']) { var a = 'a' for y in ['a'] { var b = 'b' break 2 let c = 'c' } let d = 'd' } 0").equals("0"); + // code("for (var x = 0; x < 2; ++x) { var a = 'a' { var b = 'b' break } let d = 'd' } 0").equals("0"); + code("for (var x = 0; x < 2; ++x) { var a = 'a' for (var y = 0; y < 2; ++y) { var b = 'b' break } var d = 'd' } return 0;").equals("0"); + // code("for (var x = 0; x < 2; ++x) { var a = 'a' for (var y = 0; y < 2; ++y) { var b = 'b' break 2 var c = 'c' } var d = 'd' } return 0;").equals("0"); + // code("while (true) { break 0 }").error(ls::Error::Type::BREAK_LEVEL_ZERO, {}); + // code("while (true) { continue 0 }").error(ls::Error::Type::CONTINUE_LEVEL_ZERO, {}); + + } +} diff --git a/src/test/java/test/TestMain.java b/src/test/java/test/TestMain.java new file mode 100644 index 00000000..0028ea01 --- /dev/null +++ b/src/test/java/test/TestMain.java @@ -0,0 +1,49 @@ +package test; + +import java.util.Locale; + +import org.junit.Assert; + +public class TestMain { + + public static void main(String[] args) throws Exception { + + Locale.setDefault(Locale.FRENCH); + + Locale currentLocale = Locale.getDefault(); + System.out.println("Locale = "); + System.out.println(currentLocale.getDisplayLanguage()); + System.out.println(currentLocale.getDisplayCountry()); + System.out.println(currentLocale.getLanguage()); + System.out.println(currentLocale.getCountry()); + System.out.println(System.getProperty("user.country")); + System.out.println(System.getProperty("user.language")); + + System.out.println("Start tests..."); + + // TestCommon.loadReferenceOperations(); + + // new TestCommon().code("abs = 2").equals("10"); + new TestGeneral().run(); + new TestNumber().run(); + new TestString().run(); + new TestArray().run(); + new TestMap().run(); + new TestObject().run(); + new TestClass().run(); + new TestComments().run(); + new TestOperators().run(); + new TestReference().run(); + new TestGlobals().run(); + new TestIf().run(); + new TestLoops().run(); + new TestFunction().run(); + new TestJSON().run(); + new TestFiles().run(); + new TestEuler().run(); + + TestCommon.ouputOperationsFile(); + Assert.assertTrue(TestCommon.summary()); + + } +} diff --git a/src/test/java/test/TestMap.java b/src/test/java/test/TestMap.java new file mode 100644 index 00000000..18eeed1b --- /dev/null +++ b/src/test/java/test/TestMap.java @@ -0,0 +1,127 @@ +package test; + +public class TestMap extends TestCommon { + + public void run() { + header("Map"); + + section("Constructor"); + // code("[:]").equals("[:]"); + code("return []").equals("[]"); + code("return [1: 1, 2: 2]").equals("[1 : 1, 2 : 2]"); + code("return [1: 1, 2: 'a']").equals("[1 : 1, 2 : a]"); + code("return ['1': 'a', '1': 'b', '1': 'c']").equals("[1 : c]"); + + section("Map::to_bool()"); + // code("![:]").equals("true"); + code("return ![]").equals("true"); + code("return ![2: 2]").equals("false"); + code("if ([2: 2]) { return 12 } else { return 5 }").equals("12"); + + section("Array of maps"); + code("return [[], [1: 1], [1: 2]]").equals("[[], [1 : 1], [1 : 2]]"); + // code("return [[:], [1: 1], [1: 2]]").equals("[[:], [1: 1], [1: 2]]"); + code("var m = ['a': 'b'] return [m]").equals("[[a : b]]"); + + /* + * Operators + */ + var maps = new String[] {"[5: 5]", "[5: 9.99]", "[5: 'abc']", "[9.99: 5]", "[9.99: 9.99]", "[9.99: 'abc']", "['abc': 5]", "['abc': 9.99]", "['abc': 'abc']"}; + + section("Map.operator =="); + code("return ['a': 'b'] == [1: 1]").equals("false"); + code("return ['a': 'b'] == ['a': 'b']").equals("true"); + code("return ['a': 'b'] == ['a': 'b', 'c': 'd']").equals("false"); + // code("var x = ['a' : 'b'] var y = [1 : 1] return x.clear() == y.clear()").equals("true"); + for (var m1 : maps) + for (var m2 : maps) + code("return " + m1 + " == " + m2).equals(m1 == m2 ? "true" : "false"); + code("return [12.5: 9.99] == 'hello'").equals("false"); + + // section("Map.operator <"); + // code("return ['a': 1, 'b': 2] < ['a': 1, 'b': 3]").equals("true"); + // code("return [1: 1, 2: 0] < [1: 1, 2: true]").equals("false"); + // code("return [1: 1, 2: 0.5] < [1: 1, 2: true]").equals("false"); + // code("return [1: 1, 2: 'a'] < [1: 1.5, 2: 5.5]").equals("true"); + // for (var i = 0; i < maps.length; ++i) + // for (var j = 0; j < maps.length; ++j) + // code("return " + maps[i] + " < " + maps[j]).equals(i < j ? "true" : "false"); + + // section("Map.operator in"); + // code("var m = ['salut': 12] return 'salut' in m").equals("true"); + // code("var m = ['salut': 12] return 'salum' in m").equals("false"); + // code("var m = ['salut': 12] return 12 in m.values()").equals("true"); + + section("Map.operator []"); + code("var m = [1: 1] return m[1]").equals("1"); + code("var m = ['a': 'b'] return m['a']").equals("b"); + code("var m = [5: 12] return m[5]").equals("12"); + code_v1("var m = [5: 12.5] return m[5]").equals("12,5"); + code_v2_("var m = [5: 12.5] return m[5]").equals("12.5"); + code("var m = [5.5: 12] return m[5.5]").equals("12"); + code_v1("var m = [5.5: 12.5] return m[5.5]").equals("12,5"); + code_v2_("var m = [5.5: 12.5] return m[5.5]").equals("12.5"); + code("var m = ['salut': 12] return m['salut']").equals("12"); + code_v1("var m = ['salut': 12.5] return m['salut']").equals("12,5"); + code_v2_("var m = ['salut': 12.5] return m['salut']").equals("12.5"); + code("var m = ['salut': 'yolo'] return m['salut']").equals("yolo"); + code("var m = ['a': 'b'] m['a'] = 'c' return m").equals("[a : c]"); + code("var m = ['salut': 12] m['salut'] = 13 return m['salut']").equals("13"); + code("var m = ['salut': 'yo'] m['salut'] = 'ya' return m['salut']").equals("ya"); + code("var m = [5: 12] return m[5.7]").equals("12"); + // code("var m = [5: 12] m['salut']").error(ls::Error::INVALID_MAP_KEY, {"'salut'", "m", env.tmp_string->to_string()}); + // code("var m = [5.7: 'hello'] m['salut']").error(ls::Error::INVALID_MAP_KEY, {"'salut'", "m", env.tmp_string->to_string()}); + code("var m = [1: 'a', 2: 'b'] m[2] = 'B' return m").equals("[1 : a, 2 : B]"); + // code("var m = [1: 'a', 2: 'b'] m[3]").exception(ls::vm::Exception::ARRAY_OUT_OF_BOUNDS); + code("var m = [1: 2, 3: 4] m[5] = 6 return m").equals("[1 : 2, 3 : 4, 5 : 6]"); + code("var m = ['a': 2, 'b': 4] m['c'] = 6 return m").equals("[a : 2, b : 4, c : 6]"); + code_v1("var m = ['a': 2.5, 'b': 4.8] m['c'] = 6.9 return m").equals("[a : 2,5, b : 4,8, c : 6,9]"); + code_v2_("var m = ['a': 2.5, 'b': 4.8] m['c'] = 6.9 return m").equals("[a : 2.5, b : 4.8, c : 6.9]"); + code("var m = [1: 'a', 2: 'b'] m[3] = 'c' return m").equals("[1 : a, 2 : b, 3 : c]"); + code("var m = ['a': '2', 'b': '4'] m['c'] = '6' return m").equals("[a : 2, b : 4, c : 6]"); + code("var m = [1: 2, 3: 4] m[3] = 6 return m").equals("[1 : 2, 3 : 6]"); + code_v1("var m = [1: 2.5, 3: 4.5] m[3] = 6.5 return m").equals("[1 : 2,5, 3 : 6,5]"); + code_v2_("var m = [1: 2.5, 3: 4.5] m[3] = 6.5 return m").equals("[1 : 2.5, 3 : 6.5]"); + // code_v1("var m = [1.5: 2, 3.5: 4] m[3.5] = 6 return m").equals("[1,5 : 2, 3,5 : 6]"); + // code_v2_("var m = [1.5: 2, 3.5: 4] m[3.5] = 6 return m").equals("[1.5 : 2, 3.5 : 6]"); + // code_v1("var m = [1.5: 2.5, 3.5: 4.5] m[3.5] = 6.5 return m").equals("[1,5 : 2,5, 3,5 : 6,5]"); + // code_v2_("var m = [1.5: 2.5, 3.5: 4.5] m[3.5] = 6.5 return m").equals("[1.5 : 2.5, 3.5 : 6.5]"); + code("var m = ['1': 2, '3': 4] m['3'] = 6 return m").equals("[1 : 2, 3 : 6]"); + // code_v1("var m = [1.5: 'a', 2.5: 'b'] m[2.5] = 'c' return m").equals("[1,5 : a, 2,5 : c]"); + // code_v2_("var m = [1.5: 'a', 2.5: 'b'] m[2.5] = 'c' return m").equals("[1.5 : a, 2.5 : c]"); + code("return ['', [1: 2][1]]").equals("[, 2]"); + code_v1("return ['', [1: 2.5][1]]").equals("[, 2,5]"); + code_v2_("return ['', [1: 2.5][1]]").equals("[, 2.5]"); + code("var m = [] var ns = '01234566' return m[ns] = 1").equals("1"); + + section("Map.operator [] left-value"); + code("var m = [1: 2] m[1]++ return m").equals("[1 : 3]"); + code("var m = ['a': 2] m['a']++ return m").equals("[a : 3]"); + code("var k = ['a', 12][0] var m = ['a': 2] m[k]++ return m").equals("[a : 3]"); + + // section("Map.operator [] on unknown maps"); + // code("var m = ptr(['a': '2']) m['c'] = '6' return m").equals("['a': '2', 'c': '6']"); + // code("var m = ptr([2: 'a']) m[3] = 'b' return m").equals("[2: 'a', 3: 'b']"); + // code("var m = ptr([2.5: 'a']) m[3.6] = 'b' return m").equals("[2.5: 'a', 3.6: 'b']"); + // code("var m = ptr(['a': 2]) m['c'] = 6 m").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("var m = ptr(['a': 2.2]) m['c'] = 6.6 m").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("var m = ptr([2: 2]) m[3] = 6 m").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("var m = ptr([2.5: 2]) m[3.5] = 6 m").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("var m = ptr([2.5: 2.8]) m[3.5] = 6 m").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("var m = ptr([2: 2.8]) m[3] = 6 m").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("var m = ptr([2: 'a']) m['toto'] = 'b' m").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("var m = ptr([2.5: 'a']) m['toto'] = 'b' m").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + + /* + * Iteration + */ + // section("Map iteration"); + // code("for k, v in [:] { System.print(k + ' ' + v) }").output(""); + // code("for k, v in [1:2] { System.print(k + ' ' + v) }").output("1 2\n"); + // code("for k, v in [1:2,3:4] { System.print(k + ' ' + v) }").output("1 2\n3 4\n"); + // code("for k, v in [1:2,3:4,5:6] { System.print(k + ' ' + v) }").output("1 2\n3 4\n5 6\n"); + // code("for k, v in ['a':'b'] { System.print(k + ' ' + v) }").output("a b\n"); + // code("for k, v in ['a':'b','c':'d'] { System.print(k + ' ' + v) }").output("a b\nc d\n"); + // code("for k, v in ['a':'b','c':'d','e':'f'] { System.print(k + ' ' + v) }").output("a b\nc d\ne f\n"); + } +} diff --git a/src/test/java/test/TestNumber.java b/src/test/java/test/TestNumber.java new file mode 100644 index 00000000..aa3e21bd --- /dev/null +++ b/src/test/java/test/TestNumber.java @@ -0,0 +1,1040 @@ +package test; + +public class TestNumber extends TestCommon { + + public void run() throws Exception { + + // header("Numbers"); + + section("Basic numbers"); + code("return 0;").equals("0"); + code("return -1;").equals("-1"); + code("return -(-1);").equals("1"); + // code("π").almost(3.141592653589793116); + + section("Lexical errors"); + // code("12345r").error(ls::Error::Type::NUMBER_INVALID_REPRESENTATION); + // code("0b011001711").error(ls::Error::Type::NUMBER_INVALID_REPRESENTATION); + // code("0b").error(ls::Error::Type::NUMBER_INVALID_REPRESENTATION); + // code("0x").error(ls::Error::Type::NUMBER_INVALID_REPRESENTATION); + // code("0x+").error(ls::Error::Type::NUMBER_INVALID_REPRESENTATION); + // code("0b#").error(ls::Error::Type::NUMBER_INVALID_REPRESENTATION); + // code("0b'").error(ls::Error::Type::NUMBER_INVALID_REPRESENTATION); + // code("0b\"").error(ls::Error::Type::NUMBER_INVALID_REPRESENTATION); + + section("Basic operations"); + code("return 0 + 5;").equals("5"); + code("return 5 + 5;").equals("10"); + code("return 10 - 3;").equals("7"); + code("return -2 + 3;").equals("1"); + code("return 5 * 5;").equals("25"); + code_v1("return 15 / 3;").equals("5"); + code_v2_("return 15 / 3;").equals("5.0"); + code_v1("return 15 / 2;").equals("7,5"); + code_v2_("return 15 / 2;").equals("7.5"); + code("return 12 ** 2;").equals("144"); + code("return 2 ** 5;").equals("32"); + code("return 2 < 5;").equals("true"); + code("return 12 < 5;").equals("false"); + code("return 5 == 12;").equals("false"); + code("return 12 == 12;").equals("true"); + // codreturn e("0.2 + 0.1").almost(0.3); + // DISABLED_code("return |-12|;").equals("12"); + code("return -12 * 2;").equals("-24"); + code("return (-12) * 2;").equals("-24"); + code("return -12 ** 2;").equals("144"); + code("return (-12) ** 2;").equals("144"); + code("return -12 + 2;").equals("-10"); + code("var a = [2, 'a'] return [-a[0], ~a[0]] == [-2, ~2];").equals("true"); + // DISABLED_code("var a = [2, 'a'] return [-a[0], +a[0], ~a[0]] == [-2, 2, ~2];").equals("true"); + + section("Hexadecimal representation"); + // DISABLED_code("0x0").equals("0"); + // DISABLED_code("0x00000000").equals("0"); + // DISABLED_code("0x1").equals("1"); + // DISABLED_code("0x00000001").equals("1"); + // DISABLED_code("0xf").equals("15"); + // DISABLED_code("0x0000000f").equals("15"); + // DISABLED_code("-0xf").equals("-15"); + // DISABLED_code("0xff").equals("255"); + // DISABLED_code("0x10").equals("16"); + // DISABLED_code("-0xffff").equals("-65535"); + // DISABLED_code("0xffffffff").equals("4294967295"); + // DISABLED_code("0x8fa6cd83e41a6f4ec").equals("165618988158544180460"); + // DISABLED_code("-0xa71ed8fa6cd83e41a6f4eaf4ed9dff8cc3ab1e9a4ec6baf1ea77db4fa1c").equals("-72088955549248787618860543269425825306377186794534918826231778059287068"); + // DISABLED_code("0xfe54c4ceabf93c4eaeafcde94eba4c79741a7cc8ef43daec6a71ed8fa6cd8b3e41a6f4ea7f4ed9dff8cc3ab61e9a4ec6baf1ea77deb4fa1c").equals("722100440055342029825617696009879717719483550913608718409456486549003139646247155371523487552495527165084677501327990299146441654073884"); + + section("Binary representation"); + // DISABLED_code("0b0").equals("0"); + // DISABLED_code("0b00001").equals("1"); + // DISABLED_code("0b1001010110").equals("598"); + // DISABLED_code("-0b0101101001111").equals("-2895"); + // DISABLED_code("0b010101010101110101010101011111111110111110111110000000011101101010101001").equals("1574698668551521295017"); + // DISABLED_code("-0b101010101011101010101010111111111101111101111100000000111011010101010010011111100000011111111111110000").equals("-3381639641241763826573319995376"); + + section("null must not be considered as 0"); + code("return null == 0;").equals("false"); + code("return null < 0;").equals("false"); + // code("null + 5").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("5 + null").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("5 / null").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("null / 12").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("null * 5").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("5 * null").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + + section("Numbers with variables"); + code("var a = 2 return a++;").equals("2"); + code("var a = 2; return ++a;").equals("3"); + code("var a = 2 return a--;").equals("2"); + code("var a = 2; return --a;").equals("1"); + code("var a = 2 return a += 5;").equals("7"); + code("var a = 2 return a -= 5;").equals("-3"); + code("var a = 2 return a *= 5;").equals("10"); + code_v1("var a = 100 return a /= 5;").equals("20"); + code_v2_("var a = 100 return a /= 5;").equals("20.0"); + code("var a = 56 return a %= 17;").equals("5"); + code("var a = 15 return a **= 2;").equals("225"); + code_v1("var a = 1.5 return a * 0.5;").equals("0,75"); + code_v2_("var a = 1.5 return a * 0.5;").equals("0.75"); + // DISABLED_code("var i = 1m return i = i + 2m;").equals("3"); + code("var a = 10; a += 10 - 2 * 3; return a;").equals("14"); + + section("multiple operations"); + code_v1("return (33 - 2) / 2;").equals("15,5"); + code_v2_("return (33 - 2) / 2;").equals("15.5"); + code("return 12 < (45 / 4);").equals("false"); + code("return 12 == (24 / 2);").equals("true"); + // code("2.5 + 4.7").almost(7.2); + // DISABLED_code("return 2.5 × 4.7;").equals("11.75"); + code("return 5 * 2 + 3 * 4;").equals("22"); + + section("String conversions"); + // DISABLED_code("65.char()").equals("'A'"); + // DISABLED_code("char(65)").equals("'A'"); + // DISABLED_code("126.char()").equals("'~'"); + // DISABLED_code("char(128040)").equals("'🐨'"); + // DISABLED_code("126.784.char()").equals("'~'"); + // DISABLED_code("char([126.784, 'hello'][0])").equals("'~'"); + // DISABLED_code("let c = 65 (c.char())").equals("'A'"); + // DISABLED_code("let c = 65 (c.char() + '!')").equals("'A!'"); + // DISABLED_code("0x2764.char()").equals("'❤'"); + + section("Multiple precision numbers"); + // DISABLED_code("12344532132423").equals("12344532132423"); + // DISABLED_code("var a = 10m a").equals("10"); + // DISABLED_code("0m").equals("0"); + // DISABLED_code("0xf45eab5c9d13aab44376beff").equals("75628790656539575381594128127"); + // TODO floating-point multiple precision numbers + // TODO code("123456.78910m").equals("123456.7891"); + // TODO code("123456789123456789123456789.5").equals(""); + // TODO code("1234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567891234567883459720303390827584524332795121111123456788999999999999999999999999999999999.5").equals(""); + // DISABLED_code("let a = 1209876543789765432456765432087654321 a").equals("1209876543789765432456765432087654321"); + // DISABLED_code("let a = { 1209876543789765432456765432087654321 } a").equals("1209876543789765432456765432087654321"); + // DISABLED_code("var a = 5m a = 12m").equals("12"); + // DISABLED_code("var a = 5m a = 12m a").equals("12"); + // DISABLED_code("let f = -> 12m f().string()").equals("'12'"); + + section("Integer division by zero"); + // code("1 \\ 0").exception(ls::vm::Exception::DIVISION_BY_ZERO); + // code("1 % 0").exception(ls::vm::Exception::DIVISION_BY_ZERO); + + /* + * Number standard library + */ + // header("Number standard library"); + section("Constructor"); + code_v3_("return Number").equals(""); + // DISABLED_code("Number()").equals("0"); + // DISABLED_code("Number(12)").equals("12"); + // DISABLED_code("Number(12.5)").equals("12.5"); + // DISABLED_code("Number(12l)").equals("12"); + // DISABLED_code("Number(12m)").equals("12"); + // DISABLED_code("[Number(), 'str']").equals("[0, 'str']"); + // DISABLED_code("new Number").equals("0"); + // DISABLED_code("new Number()").equals("0"); + // DISABLED_code("new Number(12)").equals("12"); + // DISABLED_code("['', new Number()]").equals("['', 0]"); + // DISABLED_code("['', new Number]").equals("['', 0]"); + // DISABLED_code("['', Number()]").equals("['', 0]"); + // DISABLED_code("['', new Number(12)]").equals("['', 12]"); + // DISABLED_code("['', Number(12)]").equals("['', 12]"); + + section("Constants"); + // code("pi").almost(3.141592653589793116); + // code("['', pi]").equals("['', 3.1415926536]"); + // code("2 × pi").almost(6.283185307179586232); + // code("e").almost(2.718281828459045091); + // code("phi").almost(1.618033988749894903); + // code("epsilon").almost(0.000000000000000222); + // code("pi").almost(3.141592653589793116); + // code("e").almost(2.718281828459045091); + // code("phi").almost(1.618033988749894903); + // code("epsilon").almost(0.000000000000000222); + // code("let pi = 3 pi").equals("3"); + // code("{ let pi = 3 } pi").almost(3.141592653589793116); + + /* + * Operators + */ + section("Number.operator unary -"); + code("var a = [12, ''] var b = a[0]; return -b;").equals("-12"); + code("return -(12 ** 2);").equals("-144"); + // DISABLED_code("return -(12m ** 2);").equals("-144"); + // DISABLED_code("-100m").equals("-100"); + + section("Number.operator unary !"); + code("var a = [12, ''] var b = a[0]; return !b;").equals("false"); + + section("Number.operator unary ~"); + code("var a = [12, ''] var b = a[0]; return ~b;").equals("-13"); + code("var a = 12 return ['', ~a];").equals("[, -13]"); + + section("Number.operator ++x"); + code("var a = 20; return ++a;").equals("21"); + code("var a = 30; ++a return a;").equals("31"); + // DISABLED_code("var a = 20m; return ++a;").equals("21"); + // DISABLED_code("var a = 20m; ++a return a;").equals("21"); + // DISABLED_code("var a = 20m; let b = ++a return b;").equals("21"); + // code("++5").error(ls::Error::Type::VALUE_MUST_BE_A_LVALUE, {"5"}); + code("var a = 5 return ['', ++a];").equals("[, 6]"); + + section("Number.operator --x"); + code("var a = 20; return --a;").equals("19"); + code("var a = 30; --a return a;").equals("29"); + // code("--5").error(ls::Error::Type::VALUE_MUST_BE_A_LVALUE, {"5"}); + code("var a = 5 return ['', --a];").equals("[, 4]"); + + section("Number.operator x++"); + code("var a = 20; return a++;").equals("20"); + code("var a = 20; a++ return a;").equals("21"); + code("var a = 20; var b = a++ return b;").equals("20"); + // DISABLED_code("var a = 20m; a++").equals("20"); + // DISABLED_code("var a = 20m; a++ a").equals("21"); + // DISABLED_code("var a = 20m; var b = a++ b").equals("20"); + // code("5++").error(ls::Error::Type::VALUE_MUST_BE_A_LVALUE, {"5"}); + + section("Number.operator x--"); + code("var a = 20; return a--;").equals("20"); + code("var a = 20; a-- return a;").equals("19"); + code("var a = 20; var b = a-- return b;").equals("20"); + // DISABLED_code("var a = 20m; a--").equals("20"); + // DISABLED_code("var a = 20m; a-- a").equals("19"); + // DISABLED_code("var a = 20m; var b = a-- b").equals("20"); + // code("5--").error(ls::Error::Type::VALUE_MUST_BE_A_LVALUE, {"5"}); + + section("Number.operator in"); + // TODO idea : a in b returns true if a is a divisor of b + // code("2 in 12").error(ls::Error::Type::VALUE_MUST_BE_A_CONTAINER, {"12"}); + + section("Number.operator ="); + // DISABLED_code("var a = 1m, b = 4m; a = b").equals("4"); + + section("Number.operator =="); + code("return 12 == 12;").equals("true"); + code("return 13 == 12;").equals("false"); + // DISABLED_code("12m == 12m").equals("true"); + // DISABLED_code("13m == 12m").equals("false"); + code("return 12 ** 5 == 12 ** 5;").equals("true"); + code("return 12 ** 5 == (3 * 4) ** 5;").equals("true"); + code("return 12 ** 5 == 248832;").equals("true"); + code("return 248832 == 12 ** 5;").equals("true"); + // DISABLED_code("12m ** 5m == 12m ** 5m").equals("true"); + // DISABLED_code("12m ** 5m == (3m * 4m) ** 5m").equals("true"); + // DISABLED_code("12m ** 5m == 248832").equals("true"); + // DISABLED_code("248832 == 12m ** 5m").equals("true"); + + section("Number.operator +"); + code("return 1 + 2;").equals("3"); + code("return 1 + (2 + 3);").equals("6"); + code("return (1 + 2) + 3;").equals("6"); + code("return (1 + 2) + (3 + 4);").equals("10"); + // DISABLED_code("1m + 2m").equals("3"); + // DISABLED_code("1m + (2m + 3m)").equals("6"); + // DISABLED_code("(1m + 2m) + 3m").equals("6"); + // DISABLED_code("(1m + 2m) + (3m + 4m)").equals("10"); + code("return 15 + false;").equals("15"); + code("return 15 + true;").equals("16"); + code("var a = 15 return a + true;").equals("16"); + // DISABLED_code("10000m + 15").equals("10015"); + // DISABLED_code("let a = ['a', 12321111111111111111111111111111111321321321999999] a[1] + 123456789").equals("12321111111111111111111111111111111321321445456788"); + code("return 10000 + (-15);").equals("9985"); + // DISABLED_code("10000m + (-15)").equals("9985"); + code("return null + 2;").equals("2"); + code("return 2 + null;").equals("2"); + code("return null + null;").equals("0"); + + section("Number.operator +="); + code("var a = 15 a += true return a;").equals("16"); + // code("var a = 15$ a += []").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("var a = 15$ a += [] a").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + code("var a = 10 return a += 4;").equals("14"); + code("var a = 10 a += 4 return a;").equals("14"); + code("var a = 15 return ['', a += 7];").equals("[, 22]"); + code("var a = 10 a += 5 return a;").equals("15"); + code("var a = 10 a += 78 return a;").equals("88"); + code("var a = 10 a += (-6) return a;").equals("4"); + // DISABLED_code("var a = 10m return a += 4m;").equals("14"); + // DISABLED_code("var a = 10m a += 4m return a;").equals("14"); + // DISABLED_code("var a = 15 return ['', a += 7];").equals("['', 22]"); + // DISABLED_code("var a = 10m a += 5 return a;").equals("15"); + // DISABLED_code("var a = 10m a += 78m return a;").equals("88"); + // DISABLED_code("var a = 10m a += (-6) return a;").equals("4"); + + section("Number.operator -"); + code("return -12").equals("-12"); + code("return -0").equals("0"); + code("return -null").equals("0"); + code("return 1 - 2;").equals("-1"); + code("return 1 - (2 - 3);").equals("2"); + code("return (1 - 2) - 3;").equals("-4"); + code("return (1 - 2) - (3 - 4);").equals("0"); + code("return (10 + 10) - 1;").equals("19"); + // DISABLED_code("return 1m - 2m;").equals("-1"); + // DISABLED_code("return 1m - (2m - 3m);").equals("2"); + // DISABLED_code("return (1m - 2m) - 3m;").equals("-4"); + // DISABLED_code("return (1m - 2m) - (3m - 4m);").equals("0"); + // DISABLED_code("return (10m + 10m) - 1").equals("19"); + code("return 15 - 3;").equals("12"); + // DISABLED_code("1000m - 12").equals("988"); + // DISABLED_code("1000m - (-12)").equals("1012"); + code("return 15 - false;").equals("15"); + code("return 15 - true;").equals("14"); + code("var a = 15 return a - true;").equals("14"); + // code("12$ - []").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + code("var a = 100 return a - 20;").equals("80"); + code("return null - null;").equals("0"); + code("return 12 - null;").equals("12"); + code("return null - 12;").equals("-12"); + + section("Number.operator -="); + code("var a = 15 a -= true return a;").equals("14"); + // code("var a = 15$ a -= []").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("var a = 15$ a -= [] a").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + code("var a = 15 return ['', a -= 6];").equals("[, 9]"); + + section("Number.operator *"); + code("return 3 * 4;").equals("12"); + code("return 10 + 3 * 4;").equals("22"); + code("return (5 + 2) * (16 * 2);").equals("224"); + // DISABLED_code("3m * 4m").equals("12"); + // DISABLED_code("10m + 3m * 4m").equals("22"); + // DISABLED_code("(5m + 2m) * (16m * 2m)").equals("224"); + code("return 12 * false;").equals("0"); + code("var a = 13; return a * false;").equals("0"); + code("return 13 * true;").equals("13"); + code("return 7 * 2;").equals("14"); + code("var a = 6; return a * 3;").equals("18"); + // code("14$ * []").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // DISABLED_code("return 12344532132423 * 987657897613412;").equals("12192174652930109838844857276"); + // DISABLED_code("12344532132423m * 987657897613412m").equals("12192174652930109838844857276"); + // DISABLED_code("5 * 'yo'").equals("'yoyoyoyoyo'"); + // DISABLED_code("50m * 10").equals("500"); + // DISABLED_code("50 * 10m").equals("500"); + // DISABLED_code("let a = ['a', 12321111111111111111111111111111111321321321999999] a[1] * 123456789").equals("1521124814690000000000000000000000025951877651354934543211"); + code("return null * 2;").equals("0"); + code("return 2 * null;").equals("0"); + code("return null * null;").equals("0"); + + section("Number.operator *="); + code("var a = 15 a *= true return a;").equals("15"); + code("var a = 15 a *= false return a;").equals("0"); + code("var a = 15 a *= null return a;").equals("0"); + // code("var a = 15$ a *= []").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("var a = 15$ a *= [] a").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + code("var a = 15; return ['', a *= 2];").equals("[, 30]"); + code("var a = 5 a *= 0 return a;").equals("0"); + code("var a = 5 a *= 12 return a;").equals("60"); + code("var a = 5 a *= 5 return a;").equals("25"); + code("var a = null a *= 5 return a;").equals("0"); + code("var a = null a *= null return a;").equals("0"); + code("var a = null return a *= 5").equals("0"); + code("var a = null return a *= null").equals("0"); + // DISABLED_code("var a = 5m a *= 0 a").equals("0"); + // DISABLED_code("var a = 5m a *= 12 a").equals("60"); + // DISABLED_code("var a = 5m a *= 5m a").equals("25"); + // DISABLED_code("var a = 91591785496891278315799124157189514175m a *= 157854689278315792457851475m a").equals("14458192840057923568549758280294876918394393505787702519557158125"); + // DISABLED_code("var a = 78m a *= true a").equals("78"); + + section("Number.operator **"); + code("return 14 ** 3;").equals("2744"); + code("return 14 ** null;").equals("1"); + code("return null ** 2;").equals("0"); + code("return null ** null;").equals("1"); + code("return 0 ** 0;").equals("1"); + // DISABLED_code("return 14 ** true;").equals("14"); + // DISABLED_code("return 14 ** false;").equals("1"); + // DISABLED_code("let a = 14 return a ** false;").equals("1"); + // code("14$ ** []").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // DISABLED_code("return 2 ** 50;").equals("1.125899906842624E15"); + // DISABLED_code("return 2l ** 50;").equals("1125899906842624"); + // DISABLED_code("257l ** 20").equals("-9223372036854775808"); // overflow + // DISABLED_code("257m ** 20").equals("1580019571820317063568778786121273112555213952001"); + // DISABLED_code("2m ** 50").equals("1125899906842624"); + // DISABLED_code("(5m + 2m) ** (16m * 2m)").equals("1104427674243920646305299201"); + // code("123m ** 1900").exception(ls::vm::Exception::NUMBER_OVERFLOW); + code("var s = 0 s = 5 ** 2 return s;").equals("25"); + + section("Number.operator **="); + code("var a = 5; a **= 4 return a").equals("625"); + code("var a = 5; return a **= 4").equals("625"); + code("var a = 5; return a **= true").equals("5"); + code("var a = null a **= 5 return a").equals("0"); + code("var a = null return a **= 5").equals("0"); + // DISABLED_code("var a = 5$; a **= false").equals("1"); + // code("var a = 5$; a **= []").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // DISABLED_code("var a = 5; ['', a **= 4]").equals("['', 625]"); + + section("Number.operator %"); + code("return 721 % 57;").equals("37"); + code("return false % 3;").equals("0"); + code("return true % 3;").equals("1"); + code("var a = 721 return a % 57;").equals("37"); + code("var a = null return a % 57;").equals("0"); + // code("let a = 721$ a % []").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // DISABLED_code("721 % true").equals("0"); + // code("721$ % false").exception(ls::vm::Exception::DIVISION_BY_ZERO); + // code("let a = 721$ a % false").exception(ls::vm::Exception::DIVISION_BY_ZERO); + // DISABLED_code("let a = 721$ a % true").equals("0"); + // DISABLED_code("123456789123456789m % 234567m").equals("221463"); + // DISABLED_code("(12m ** 40m) % 234567m").equals("228798"); + // DISABLED_code("100000m % (12m ** 3m)").equals("1504"); + // DISABLED_code("(100000m * 10m) % (12m ** 3m)").equals("1216"); + // code("['salut', 123][0] % 5").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // TODO should have semantic error + // code("['salut', 'a'][0] % 5").error(ls::Error::NO_SUCH_OPERATOR, {}); + + section("Number.operator %="); + code("var a = 721 return a %= 17;").equals("7"); + // DISABLED_code("var a = 721 a %= true").equals("0"); + // code("var a = 721$ a %= []").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + + // section("Number.operator %%"); + // DISABLED_code("0 %% 1").equals("0"); + // DISABLED_code("2 %% 5").equals("2"); + // DISABLED_code("(-2) %% 5").equals("3"); + // DISABLED_code("(-12) %% 5").equals("3"); + // DISABLED_code("721 %% 57").equals("37"); + // DISABLED_code("(-721) %% 57").equals("20"); + // DISABLED_code("(-721$) %% 57$").equals("20"); + + // section("Number.operator %%="); + // DISABLED_code("var a = 0 a %%= 1").equals("0"); + // DISABLED_code("var a = 2 a %%= 5").equals("2"); + // DISABLED_code("var a = -2 a %%= 5").equals("3"); + // DISABLED_code("var a = -12 a %%= 5").equals("3"); + // DISABLED_code("var a = 721 a %%= 57").equals("37"); + // DISABLED_code("var a = -721 a %%= 57").equals("20"); + // DISABLED_code("var a = -721$ a %%= 57$").equals("20"); + + section("Number.operator /"); + code("8 / 0").equals("null"); + code("8 / null").equals("null"); + code("null / 5").equals("null"); + // code("12$ / false").exception(ls::vm::Exception::DIVISION_BY_ZERO); + // code("let a = 13$; a / false").exception(ls::vm::Exception::DIVISION_BY_ZERO); + code_v1("return 13 / true;").equals("13"); + code_v2_("return 13 / true;").equals("13.0"); + code_v1("return 14 / 2;").equals("7"); + code_v2_("return 14 / 2;").equals("7.0"); + code_v1("var a = 18; return a / 3;").equals("6"); + code_v2_("var a = 18; return a / 3;").equals("6.0"); + // code("14$ / []").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + code_v1("var a = 17, b = 5 return a / b;").equals("3,4"); + code_v2_("var a = 17, b = 5 return a / b;").equals("3.4"); + + section("Number.operator /="); + code_v1("var a = 12 a /= 3 return a;").equals("4"); + code_v2_("var a = 12 a /= 3 return a;").equals("4.0"); + code_v1("var a = 12 a /= 0.5 return a;").equals("24"); + code_v2_("var a = 12 a /= 0.5 return a;").equals("24.0"); + code_v1("var a = 12 a /= true return a;").equals("12"); + code_v2_("var a = 12 a /= true return a;").equals("12.0"); + code_v1("var a = null a /= 5 return a;").equals("0"); + code_v2_("var a = null a /= 5 return a;").equals("0.0"); + // code("var a = 12 a /= false return a;").equals("nan"); + // code("var a = 12$ a /= []").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("var a = 12$ a /= [] a").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + code_v1("var a = 15; return ['', a /= 2];").equals("[, 7,5]"); + code_v2_("var a = 15; return ['', a /= 2];").equals("[, 7.5]"); + + section("Number.operator <"); + code("return 5 < 2;").equals("false"); + code("return 2 < 5;").equals("true"); + code("return 5.1 < 2.1;").equals("false"); + code("return 2.1 < 5.1;").equals("true"); + // code("3m < 4m").equals("true"); + // code("10m < (3m * 4m)").equals("true"); + // code("(5m + 5m) < (3m * 4m)").equals("true"); + // code("(5m + 5m) < 12m").equals("true"); + // code("3m < 4").equals("true"); + + section("Number.operator <="); + code("return 5 <= 2;").equals("false"); + code("return 2 <= 5;").equals("true"); + code("return 5.1 <= 2.1;").equals("false"); + code("return 2.1 <= 5.1;").equals("true"); + code("return 3 <= 4;").equals("true"); + code("return 3 <= [];").equals("false"); + + section("Number.operator >"); + code("return 5 > 2;").equals("true"); + code("return 2 > 5;").equals("false"); + code("return 5.1 > 2.1;").equals("true"); + code("return 2.1 > 5.1;").equals("false"); + // code("12 > 5m").equals("true"); + code("return [] > true;").equals("false"); + // code("-100m > 0").equals("false"); + + section("Number.operator >="); + code("return 5 >= 2;").equals("true"); + code("return 2 >= 5;").equals("false"); + code("return 5.1 >= 2.1;").equals("true"); + code("return 2.1 >= 5.1;").equals("false"); + + // section("Number.operator \\"); + // code("10 \\ 2").equals("5"); + // code("10 \\ 4").equals("2"); + // code("2432431 \\ 2313").equals("1051"); + // code("let a = 420987$ a \\ 546$").equals("771"); + // code("420987$ \\ 12").equals("35082"); + // code("12345678912345l \\ 1234").equals("10004602035"); + // code("12$ \\ false").exception(ls::vm::Exception::DIVISION_BY_ZERO); + // code("let a = 13$; a \\ false").exception(ls::vm::Exception::DIVISION_BY_ZERO); + // code("13$ \\ true").equals("13"); + // code("17$ \\ 4").equals("4"); + // code("let a = 10.7$; a \\ true").equals("10"); + // code("let a = 10$; a \\ 4").equals("2"); + // code("14$ \\ []").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("67.89$ \\ 1").equals("67"); + // code("['', 10 \\ 2]").equals("['', 5]"); + // code("['', 10$ \\ 2]").equals("['', 5]"); + + // section("Number.operator \\="); + // code("var a = 12 a \\= 5").equals("2"); + // code("var a = 12$ a \\= 5").equals("2"); + // code("var a = 30$ a \\= 4 a").equals("7"); + // code("var a = 12$ a \\= true a").equals("12"); + // code("var a = 12$ a \\= false a").exception(ls::vm::Exception::DIVISION_BY_ZERO); + // code("var a = 12$ a \\= []").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("var a = 12$ a \\= [] a").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("var a = 12 ['', a \\= 5]").equals("['', 2]"); + + section("Number.operator &"); + code("return 0 & 0;").equals("0"); + code("return 1 & 0;").equals("0"); + code("return 1 & 1;").equals("1"); + code("return 5 & 12;").equals("4"); + code("return 87619 & 18431;").equals("17987"); + code("return 87619 & [18431, ''][0];").equals("17987"); + code("var a = 87619 return a &= 18431;").equals("17987"); + code("var a = 87619 a &= 18431 return a;").equals("17987"); + code("return 87619 & 18431;").equals("17987"); + // code("87619$ &= 18431").error(ls::Error::VALUE_MUST_BE_A_LVALUE, {"87619"}); + code("var a = 87619 a &= 18431 return a;").equals("17987"); + // code("[12, 'hello'][1] & 5").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + // code("var a = [12, 'hello'][1] a &= 18431 a").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + + section("Number.operator |"); + code("return 0 | 0;").equals("0"); + code("return 1 | 0;").equals("1"); + code("return 1 | 1;").equals("1"); + code("return 5 | 12;").equals("13"); + code("return [5, ''][0] | [12, ''][0];").equals("13"); + code("return 87619 | 18431;").equals("88063"); + code("var a = 87619 return a |= 18431;").equals("88063"); + code("var a = 87619 a |= 18431 return a;").equals("88063"); + code("return [87619, ''][0] | 18431;").equals("88063"); + // code("87619$ |= 18431").error(ls::Error::VALUE_MUST_BE_A_LVALUE, {"87619"}); + code("var a = 87619 a |= 18431 return a;").equals("88063"); + // code("[12, 'hello'][1] | 5").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + + section("Number.operator ^"); + code("return 0 ^ 0;").equals("0"); + code("return 1 ^ 0;").equals("1"); + code("return 1 ^ 1;").equals("0"); + code("return 5 ^ 12;").equals("9"); + code("return 87619 ^ 18431;").equals("70076"); + code("return [87619, ''][0] ^ [18431, ''][0];").equals("70076"); + code_v1("var a = 5 a ^= 2 return a;").equals("25"); // In LS 1.0, ^= was power equals + code_v2_("var a = 87619 return a ^= 18431;").equals("70076"); + code_v2_("var a = 87619 a ^= 18431 return a;").equals("70076"); + code("return [87619, ''][0] ^ 18431;").equals("70076"); + // code("87619$ ^= 18431").error(ls::Error::VALUE_MUST_BE_A_LVALUE, {"87619"}); + code_v2_("var a = 87619 a ^= 18431 return a;").equals("70076"); + // code("[12, 'hello'][1] ^ 5").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + + section("Number.operator <<"); + code("return 0 << 0;").equals("0"); + code("return 1 << 0;").equals("1"); + code("return 123456 << 0;").equals("123456"); + code("return 0 << 1;").equals("0"); + code("return 0 << 12;").equals("0"); + code("return 1 << 8;").equals("256"); + code("return 123 << 12;").equals("503808"); + code("return [123, ''][0] << 12;").equals("503808"); + code("var a = 123 return a <<= 11;").equals("251904"); + code("var a = 123 a <<= 13 return a;").equals("1007616"); + code("var a = [123, ''] return a[0] <<= 13;").equals("1007616"); + code("var a = 123 return ['', a <<= 13];").equals("[, 1007616]"); + // code("'salut' << 5").exception(ls::vm::Exception::NO_SUCH_OPERATOR); + + section("Number.operator >>"); + code("return 0 >> 0;").equals("0"); + code("return 1 >> 0;").equals("1"); + code("return 123456 >> 0;").equals("123456"); + code("return 0 >> 1;").equals("0"); + code("return 0 >> 12;").equals("0"); + code("return 155 >> 3;").equals("19"); + code("return -155 >> 3;").equals("-20"); + code("return 12345 >> 8;").equals("48"); + code("return 123123123 >> 5;").equals("3847597"); + code("return [123123123, ''][0] >> 5;").equals("3847597"); + code("var a = 123123123 return a >>= 6;").equals("1923798"); + code("var a = 123123123 a >>= 7 return a;").equals("961899"); + code("var a = [123123123, ''] return a[0] >>= 7;").equals("961899"); + code("var a = 12345 return ['', a >>= 8];").equals("[, 48]"); + // code("'salut' >> 5").error(ls::Error::NO_SUCH_OPERATOR, {env.tmp_string->to_string(), ">>", env.integer->to_string()}); + + section("Number.operator >>>"); + code("return 155 >>> 3;").equals("19"); + code("return -155 >>> 3;").equals("536870892"); + code("return [-155, ''][0] >>> 3;").equals("536870892"); + code("var a = -155 return a >>>= 4;").equals("268435446"); + code("var a = -155 a >>>= 5 return a;").equals("134217723"); + code("var a = [-155, ''] return a[0] >>>= 5;").equals("134217723"); + code("var a = -155 return ['', a >>>= 5];").equals("[, 134217723]"); + // code("'salut' >>> 5").error(ls::Error::NO_SUCH_OPERATOR, {env.tmp_string->to_string(), ">>>", env.integer->to_string()}); + + section("Not a statement errors"); + code("null; return null;").equals("null"); + code("(null); return null;").equals("null"); + code("((null)); return null;").equals("null"); + code("true; return null;").equals("null"); + code("false; return null;").equals("null"); + code("'salut'; return null;").equals("null"); + code("var a; a; return null;").equals("null"); + code("12; return null;").equals("null"); + code("12 && 5; return null;").equals("null"); + code("12 + 5; return null;").equals("null"); + code("12 - 5; return null;").equals("null"); + code("12 * 5; return null;").equals("null"); + code("12 / 5; return null;").equals("null"); + code("12 % 5; return null;").equals("null"); + code("12 ** 5; return null;").equals("null"); + code("12 ^ 5; return null;").equals("null"); + code("12 & 5; return null;").equals("null"); + code("12 | 5; return null;").equals("null"); + code("12 < 5; return null;").equals("null"); + code("12 > 5; return null;").equals("null"); + code("12 <= 5; return null;").equals("null"); + code("12 >= 5; return null;").equals("null"); + code("12 == 5; return null;").equals("null"); + code("12 === 5; return null;").equals("null"); + code("(12 && 5); return null;").equals("null"); + code("true ? 1 : 2; return null;").equals("null"); + code("(true ? 1 : 2); return null;").equals("null"); + + /* + * Methods + */ + section("Number.abs()"); + // code("return abs;").equals(""); + code("return abs(-12);").equals("12"); + code_v1("return abs(-19.5);").equals("19,5"); + code_v2_("return abs(-19.5);").equals("19.5"); + code("return abs(12);").equals("12"); + // code("return abs(-16436435l)").equals("16436435"); + code_v1("return abs(-12.67);").equals("12,67"); + code_v2_("return abs(-12.67);").equals("12.67"); + code("return abs(['a', -15][1]);").equals("15"); + // code("return (-17).abs()").equals("17"); + // code("return (-19.5).abs()").equals("19.5"); + // code("return 12.abs").equals(""); + // code("abs([1, 'salut'][1])").exception(ls::vm::Exception::WRONG_ARGUMENT_TYPE); + + section("Number.exp()"); + code_v1("return exp(0)").equals("1"); + code_v2_("return exp(0)").equals("1.0"); + code_v1("return exp(1)").equals("2,718"); + code_v2_("return exp(1)").almost(Math.E); + code_v1("return exp(4)").equals("54,598"); + code_v2_("return exp(4)").almost(54.598150033144236204); + code_v1("return exp(4.89)").equals("132,954"); + code_v2_("return exp(4.89)").almost(132.953574051282743085); + code_v1("return exp(-2.97)").equals("0,051"); + code_v2_("return exp(-2.97)").almost(0.051303310331919108); + code_v1("return exp(['a', 7.78][1])").equals("2 392,275"); + code_v2_("return exp(['a', 7.78][1])").almost(2392.274820537377763685); + // code("return 0.exp();").equals("1"); + // code("return 1.exp();").almost(Math.E); + // code("return 7.exp();").almost(1096.633158428458500566); + // code("return (-7).exp();").almost(0.000911881965554516); + // code("return (-3.33).exp();").almost(0.035793105067655297); + code_v1("return E ** 5;").equals("148,413"); + code_v2_("return E ** 5;").almost(148.413159102576571513); + + section("Number.floor()"); + code("return floor(5.9);").equals("5"); + code("var a = 5 return floor(a);").equals("5"); + code("var a = 5.4 return floor(a);").equals("5"); + code("return floor(['a', -14.7][1]);").equals("-15"); + code("return floor(5.5);").equals("5"); + code("return floor(1.897)").equals("1"); + code("return floor(3.01)").equals("3"); + + section("Number.round()"); + code("return round(5.7)").equals("6"); + code("return round(5.4)").equals("5"); + code("return round(['a', -15.89][1])").equals("-16"); + code("return round(12)").equals("12"); + code("return round(-1000)").equals("-1000"); + code("return round(1.897)").equals("2"); + code("return round(3.01)").equals("3"); + + section("Number.ceil()"); + code("return ceil(5.1)").equals("6"); + code("return ceil(188)").equals("188"); + code("return ceil(1.897)").equals("2"); + code("return ceil(3.01)").equals("4"); + + section("Number.max()"); + code("return max(8, 5)").equals("8"); + code("return max(8, 88)").equals("88"); + code("return max(5, 12);").equals("12"); + code_v1("return max(5.0, 12);").equals("12"); + code_v2_("return max(5.0, 12);").equals("12.0"); + code_v1("return max(75.7, 12);").equals("75,7"); + code_v2_("return max(75.7, 12);").almost(75.7); + code_v1("return max(5, 12.451);").equals("12,451"); + code_v2_("return max(5, 12.451);").almost(12.451); + code("return max([5, 'a'][0], 4);").equals("5"); + code("return max([5, 'a'][0], 76);").equals("76"); + code("return max(4, [5, 'a'][0]);").equals("5"); + code("return max(77, [5, 'a'][0]);").equals("77"); + code("return max([55, 'a'][0], [5, 'a'][0]);").equals("55"); + code_v1("return max(5, 12.8);").equals("12,8"); + code_v2_("return max(5, 12.8);").equals("12.8"); + code_v1("return max(15.7, 12.8);").equals("15,7"); + code_v2_("return max(15.7, 12.8);").equals("15.7"); + code_v1("return max([15.7, ''][0], 12.8);").equals("15,7"); + code_v2_("return max([15.7, ''][0], 12.8);").equals("15.7"); + code_v1("return max(5.5, [12.8, ''][0]);").equals("12,8"); + code_v2_("return max(5.5, [12.8, ''][0]);").equals("12.8"); + // code("return 2.max([7.5, ''][0]);").equals("7.5"); + // code("return [2, ''][0].max([7.5, ''][0]);").equals("7.5"); + // code("2.max([7.5, ''][1])").exception(ls::vm::Exception::WRONG_ARGUMENT_TYPE); + // code("return max(5l, 10.5)").equals("10.5"); + // code("return max(5l, 10)").equals("10"); + // code("return max(true, 10l)").equals("10"); + // code("max('string', 12)").error(ls::Error::METHOD_NOT_FOUND, {"max(" + env.tmp_string->to_string() + ", " + env.integer->to_string() + ")"}); + + section("Number.min()"); + code("return min(8, 5)").equals("5"); + code("return min(8, 88)").equals("8"); + code("return min(5, 12)").equals("5"); + code_v1("return min(75.7, 12)").equals("12"); + code_v2_("return min(75.7, 12)").almost(12.0); + code_v1("return min(5, 12.451)").equals("5"); + code_v2_("return min(5, 12.451)").almost(5.0); + code("return min([5, 'a'][0], 4)").equals("4"); + code("return min([5, 'a'][0], 76)").equals("5"); + code("return min(4, [5, 'a'][0])").equals("4"); + code("return min(77, [5, 'a'][0])").equals("5"); + code("return min([55, 'a'][0], [5, 'a'][0])").equals("5"); + code_v1("return min(5, 12.8)").equals("5"); + code_v2_("return min(5, 12.8)").equals("5.0"); + code_v1("return min(15.7, 12.8)").equals("12,8"); + code_v2_("return min(15.7, 12.8)").equals("12.8"); + code_v1("return min([15.7, ''][0], 12.8)").equals("12,8"); + code_v2_("return min([15.7, ''][0], 12.8)").equals("12.8"); + code_v1("return min(5.5, [12.8, ''][0])").equals("5,5"); + code_v2_("return min(5.5, [12.8, ''][0])").equals("5.5"); + // code("return min(5l, 10.5);").equals("5"); + // code("return min(5l, 10);").equals("5"); + // code("return min(true, 10l);").equals("1"); + // code("min('string', 12)").error(ls::Error::METHOD_NOT_FOUND, {"min(" + env.tmp_string->to_string() + ", " + env.integer->to_string() + ")"}); + + section("Number.cos()"); + code_v1("return cos(0)").equals("1"); + code_v2_("return cos(0)").equals("1.0"); + code_v1("return cos(2.5)").equals("-0,801"); + code_v2_("return cos(2.5)").almost(Math.cos(2.5)); + code_v1("return cos(PI)").equals("-1"); + code_v2_("return cos(PI)").equals("-1.0"); + code_v1("return cos(PI / 2)").equals("0"); + code_v2_("return cos(PI / 2)").almost(0.0); + // code("return π.cos()").equals("-1"); + // code("return ['', π][1].cos()").equals("-1"); + code_v1("return cos(['', PI][1]);").equals("-1"); + code_v2_("return cos(['', PI][1]);").equals("-1.0"); + code_v1("return cos(PI);").equals("-1"); + code_v2_("return cos(PI);").equals("-1.0"); + + section("Number.acos()"); + code_v1("return acos(1)").equals("0"); + code_v2_("return acos(1)").equals("0.0"); + code_v1("return acos(-1)").equals("3,142"); + code_v2_("return acos(-1)").almost(Math.PI); + code_v1("return acos(0)").equals("1,571"); + code_v2_("return acos(0)").almost(Math.PI / 2); + // code("return (-0.33).acos()").almost(1.907099901948877019); + code_v1("return acos(['y', 0][1])").equals("1,571"); + code_v2_("return acos(['y', 0][1])").almost(Math.PI / 2); + + section("Number.sin()"); + code_v1("return sin(0)").equals("0"); + code_v2_("return sin(0)").equals("0.0"); + code_v1("return sin(2.5)").equals("0,598"); + code_v2_("return sin(2.5)").almost(Math.sin(2.5)); + code_v1("return sin(PI)").equals("0"); + code_v2_("return sin(PI)").almost(0.0); + code_v1("return sin(PI / 2)").equals("1"); + code_v2_("return sin(PI / 2)").equals("1.0"); + code_v1("return sin(- PI / 2)").equals("-1"); + code_v2_("return sin(- PI / 2)").equals("-1.0"); + code_v1("return sin(['', PI / 2][1])").equals("1"); + code_v2_("return sin(['', PI / 2][1])").equals("1.0"); + code_v1("return sin(PI / 2)").equals("1"); + code_v2_("return sin(PI / 2)").equals("1.0"); + + section("Number.tan()"); + code_v1("return tan(0)").equals("0"); + code_v2_("return tan(0)").equals("0.0"); + code_v1("return tan(2.5)").equals("-0,747"); + code_v2_("return tan(2.5)").almost(Math.tan(2.5)); + code_v1("return tan(PI)").equals("-0"); + code_v2_("return tan(PI)").almost(0.0); + code_v1("return tan(PI / 4)").equals("1"); + code_v2_("return tan(PI / 4)").almost(1.0); + code_v1("return tan(- PI / 4)").equals("-1"); + code_v2_("return tan(- PI / 4)").almost(-1.0); + code_v1("return tan(['', PI / 4][1])").equals("1"); + code_v2_("return tan(['', PI / 4][1])").almost(1.0); + + section("Number.asin()"); + code_v1("return asin(0)").equals("0"); + code_v2_("return asin(0)").equals("0.0"); + code_v1("return asin(-1)").equals("-1,571"); + code_v2_("return asin(-1)").almost(-Math.PI / 2); + code_v1("return asin(1)").equals("1,571"); + code_v2_("return asin(1)").almost(Math.PI / 2); + // code("return 0.33.asin()").almost(0.33630357515398035); + code_v1("return asin(['y', -1][1])").equals("-1,571"); + code_v2_("return asin(['y', -1][1])").almost(-Math.PI / 2); + + section("Number.atan()"); + code_v1("return atan(1)").equals("0,785"); + code_v2_("return atan(1)").almost(Math.PI / 4); + code_v1("return atan(-1)").equals("-0,785"); + code_v2_("return atan(-1)").almost(-Math.PI / 4); + code_v1("return atan(0.5)").equals("0,464"); + code_v2_("return atan(0.5)").almost(0.463647609000806094); + // code("return 0.atan()").equals("0"); + code_v1("return atan(['y', 0.5][1])").equals("0,464"); + code_v2_("return atan(['y', 0.5][1])").almost(0.463647609000806094); + + section("Number.atan2()"); + code_v1("return atan2(1, 1)").equals("0,785"); + code_v2_("return atan2(1, 1)").almost(Math.PI / 4); + code_v1("return atan2(150.78, 150.78)").equals("0,785"); + code_v2_("return atan2(150.78, 150.78)").almost(Math.PI / 4); + code_v1("return atan2(1, 0)").equals("1,571"); + code_v2_("return atan2(1, 0)").almost(Math.PI / 2); + code_v1("return atan2(-1, 0)").equals("-1,571"); + code_v2_("return atan2(-1, 0)").almost(-Math.PI / 2); + code_v1("return atan2(0, 1)").equals("0"); + code_v2_("return atan2(0, 1)").equals("0.0"); + code_v1("return atan2(0, -1)").equals("3,142"); + code_v2_("return atan2(0, -1)").almost(Math.PI); + code_v1("return atan2(12.12, 42.42)").equals("0,278"); + code_v2_("return atan2(12.12, 42.42)").almost(0.278299659005111333); + // code("return 1.atan2(1)").almost(Math.PI / 4); + // code("return ['', -1][1].atan2(1)").almost(-Math.PI / 4); + // code("return 1.atan2(['', -1][1])").almost(3 * Math.PI / 4); + // code("return ['', -1][1].atan2(['', -1][1])").almost(-3 * Math.PI / 4); + code_v1("return atan2(1, 1)").equals("0,785"); + code_v2_("return atan2(1, 1)").almost(Math.PI / 4); + code_v1("return atan2(['', -1][1], 1)").equals("-0,785"); + code_v2_("return atan2(['', -1][1], 1)").almost(-Math.PI / 4); + code_v1("return atan2(1, ['', -1][1])").equals("2,356"); + code_v2_("return atan2(1, ['', -1][1])").almost(3 * Math.PI / 4); + code_v1("return atan2(['', -1][1], ['', -1][1])").equals("-2,356"); + code_v2_("return atan2(['', -1][1], ['', -1][1])").almost(-3 * Math.PI / 4); + // code("return atan2(1, true)").almost(Math.PI / 4); + // code("return atan2(true, false)").almost(Math.PI / 2); + + section("Number.cbrt()"); + code("return cbrt(125)").almost(5.0); + code("return cbrt(1000)").almost(10.0); + code("return cbrt(1728)").almost(12.0, 1e-14); + // code("return 1728.cbrt()").almost(12.0, 0.00000000000001); + code("return cbrt(['', 1728][1])").almost(12.0, 0.00000000000001); + // code("return ['', 1728][1].cbrt()").almost(12.0, 0.00000000000001); + + // section("Number.int()"); + // code("int(15.1)").equals("15"); + // code("int(15.5)").equals("15"); + // code("int(15.9)").equals("15"); + + // section("Number.isInteger()"); + // code("isInteger(12)").equals("true"); + // code("isInteger(0)").equals("true"); + // code("isInteger(-5)").equals("true"); + // code("isInteger(12.9)").equals("false"); + // code("isInteger(-5.2)").equals("false"); + // code("isInteger(π)").equals("false"); + // code("12.isInteger()").equals("true"); + // code("12.5.isInteger()").equals("false"); + // code("[12, 0][0].isInteger()").equals("true"); + // code("[12.5, 0][0].isInteger()").equals("false"); + + // section("Number.fold"); + // code("1234567.fold((x, y) -> x + y, 0)").equals("28"); + // code("1234567.fold((x, y) -> x + y, 1000)").equals("1028"); + // code("1234567.fold((x, y) -> x * y, 1)").equals("5040"); + // code("1234567.fold((x, y) -> x + y ** 2, 0)").equals("140"); + + section("Number.hypot"); + code_v1("return hypot(3, 4)").equals("5"); + code_v2_("return hypot(3, 4)").equals("5.0"); + // code("return 3.hypot(4)").equals("5"); + code_v1("return hypot(34, 74)").equals("81,437"); + code_v2_("return hypot(34, 74)").almost(81.437092286); + code_v1("return hypot([34, ''][0], 74)").equals("81,437"); + code_v2_("return hypot([34, ''][0], 74)").almost(81.437092286); + + section("Number.signum"); + // code("0.signum()").equals("0"); + // code("-0.signum()").equals("0"); + // code("12.signum()").equals("1"); + // code("12.5.signum()").equals("1"); + // code("-12.signum()").equals("-1"); + // code("-12.5.signum()").equals("-1"); + code("return signum(0)").equals("0"); + code("return signum(12)").equals("1"); + code("return signum(-17)").equals("-1"); + code("return signum(-12.5)").equals("-1"); + code("return signum(85)").equals("1"); + + section("Number.sqrt"); + code_v1("return sqrt(2)").equals("1,414"); + code_v2_("return sqrt(2)").almost(Math.sqrt(2)); + // code("return sqrt(123456789123456789123456789)").equals("11111111066111"); + // code("return sqrt(55m ** 20m)").equals("253295162119140625"); + // code("return sqrt(12m + 5m)").equals("4"); + // code("return var n = 12; n.sqrt()").equals("3.4641016151"); + // code("return let f = sqrt f(5)").equals("2.2360679775"); + code_v1("return sqrt(16)").equals("4"); + code_v2_("return sqrt(16)").equals("4.0"); + code_v1("return sqrt(25)").equals("5"); + code_v2_("return sqrt(25)").equals("5.0"); + + section("Number.toDegrees"); + // code("return π.toDegrees()").equals("180"); + // code("return (π / 2).toDegrees()").equals("90"); + // code("return (-π / 2).toDegrees()").equals("-90"); + // code("return 0.toDegrees()").equals("0"); + code_v1("return toDegrees(PI)").equals("180"); + code_v2_("return toDegrees(PI)").equals("180.0"); + code_v1("return toDegrees(PI / 2)").equals("90"); + code_v2_("return toDegrees(PI / 2)").equals("90.0"); + code_v1("return toDegrees(-PI / 2)").equals("-90"); + code_v2_("return toDegrees(-PI / 2)").equals("-90.0"); + code_v1("return toDegrees(0)").equals("0"); + code_v2_("return toDegrees(0)").equals("0.0"); + + section("Number.toRadians"); + // code("return 180.toRadians()").almost(Math.PI); + // code("return 90.toRadians()").almost(Math.PI / 2); + // code("return (-90).toRadians()").almost(-Math.PI / 2); + // code("return 0.toRadians()").equals("0"); + code_v1("return toRadians(180)").equals("3,142"); + code_v2_("return toRadians(180)").almost(Math.PI); + code_v1("return toRadians(90)").equals("1,571"); + code_v2_("return toRadians(90)").almost(Math.PI / 2); + code_v1("return toRadians(-90)").equals("-1,571"); + code_v2_("return toRadians(-90)").almost(-Math.PI / 2); + code_v1("return toRadians(0)").equals("0"); + code_v2_("return toRadians(0)").equals("0.0"); + + section("Number.log"); + // code("1.log()").equals("0"); + code_v1("return log(1)").equals("0"); + code_v2_("return log(1)").equals("0.0"); + code_v1("return log(E)").equals("1"); + code_v2_("return log(E)").equals("1.0"); + // code("123456.log()").equals("11.7236400963"); + code_v1("return log(654321)").equals("13,391"); + code_v2_("return log(654321)").almost(13.3913533357); + code_v1("return log([55555, ''][0])").equals("10,925"); + code_v2_("return log([55555, ''][0])").almost(10.9251288); + + section("Number.log10"); + code_v1("return log10(10)").equals("1"); + code_v2_("return log10(10)").equals("1.0"); + // code("return 1.log10()").equals("0"); + // code("return 123456.log10()").equals("5.0915122016"); + code_v1("return log10(654321)").equals("5,816"); + code_v2_("return log10(654321)").almost(5.8157908589); + code_v1("return log10([55555, ''][0])").equals("4,745"); + code_v2_("return log10([55555, ''][0])").almost(4.7447231519); + + section("Number.pow"); + // code("2.pow(10)").equals("1024"); + code_v1("return pow(5, 3)").equals("125"); + code_v2_("return pow(5, 3)").equals("125.0"); + code_v1("return pow(2, 10)").equals("1 024"); + code_v2_("return pow(2, 10)").equals("1024.0"); + // code("pow([10, ''][0], 5)").equals("100000"); + // code("3000.pow(3)").equals("2147483648"); + // code("return pow(3000, 3)").equals("2147483648"); + // code("3000l.pow(3)").equals("27000000000"); + + // section("Object-like calls"); + // code("(-12).abs()").equals("12"); + // code("π.cos()").equals("-1"); + // code("(π / 2).sin()").equals("1"); + // code("12.sqrt()").almost(3.464101615137754386); + // code("12.8.floor()").equals("12"); + // code("-12.8.floor()").equals("-12"); + // code("(-12.8).floor()").equals("-13"); + // code("12.2.ceil()").equals("13"); + // code("12.8.round()").equals("13"); + // code("-12.8.round()").equals("-13"); + // code("2.pow(10)").equals("1024"); + // code("0.isInteger()").equals("true"); + // code("56.7.isInteger()").equals("false"); + // code("(-56.7).isInteger()").equals("false"); + // code("3.max(5)").equals("5"); + // code("5.max(3)").equals("5"); + + // section("Combinated"); + // code("3.max(5).min(2)").equals("2"); + // code("3.max(5).max(10).max(12)").equals("12"); + // code("10.max(5).max(8.7).max(-3.91)").equals("10"); + // code("10.sqrt().cos()").almost(-0.99978607287932586); + + // section("Number.isPrime()"); + // code("0.isPrime()").equals("false"); + // code("1.isPrime()").equals("false"); + // code("2.isPrime()").equals("true"); + // code("3.isPrime()").equals("true"); + // code("4.isPrime()").equals("false"); + // code("5.isPrime()").equals("true"); + // code("1993.isPrime()").equals("true"); + // code("3972049.isPrime()").equals("false"); + // code("(1993l).isPrime()").equals("true"); + // code("4398042316799.isPrime()").equals("true"); + // code("(4398042316799m).isPrime() >= 1").equals("true"); + // code("359334085968622831041960188598043661065388726959079837.isPrime()").equals("1"); + // code("(146m ** 45m).isPrime()").equals("0"); + // code("1993l.isPrime()").equals("true"); + // code("1993m.isPrime()").equals("2"); + + // section("Number.rand()"); + // code("var a = rand() a >= 0 and a <= 1").equals("true"); + // code("var a = rand() a > 1").equals("false"); + // code("var a = randInt(2067, 2070) a >= 2067 and a < 2070").equals("true"); + // code("var a = randFloat(500, 510) a >= 500 and a < 510 and !a.isInteger()").equals("true"); + } +} diff --git a/src/test/java/test/TestObject.java b/src/test/java/test/TestObject.java new file mode 100644 index 00000000..75c71259 --- /dev/null +++ b/src/test/java/test/TestObject.java @@ -0,0 +1,575 @@ +package test; + +import leekscript.common.Error; + +public class TestObject extends TestCommon { + + public void run() { + + section("Objects"); + code_v3_("return Object()").equals("{}"); + code_v3_("return new Object").equals("{}"); + code_v3_("return new Object()").equals("{}"); + code_v2_("return {}").equals("{}"); + code_v2_("return {a: 12}").equals("{a: 12}"); + code_v2_("return {a: 12, b: 5}").equals("{a: 12, b: 5}"); + code_v2_("return {a: {}, b: []}").equals("{a: {}, b: []}"); + code_v2_("var a = {} return a").equals("{}"); + code_v2_("var a = {b: 12, c: 5} return a").equals("{b: 12, c: 5}"); + + section("Objects with functions"); + code_v2_("var f = function(obj) { return obj.a } return f({a: 'foo'})").equals("foo"); + code_v2_("var f = function(obj) { return obj.a } return [f({a: 'foo'}), f({a: 'bar'})]").equals("[foo, bar]"); + //code("var f = function(obj) { return obj.a } [f(12), f({a: 'bar'})]").error(ls::Error::NO_SUCH_ATTRIBUTE, {"a", "Number"}); + + section("No commas"); + code_v2_("return {a: 12 b: 5}").equals("{a: 12, b: 5}"); + code_v2_("return {a: 12 - 2 yo: -6}").equals("{a: 10, yo: -6}"); + code_v2_("return {a: 12 b: 'yo' c: true d: [1 2 3]}").equals("{a: 12, b: yo, c: true, d: [1, 2, 3]}"); + + section("Classes"); + code_v2_("class A { } return new A();").equals("A {}"); + code_v2_("class A { a = 10 } var a = [new A()]; a[0].a++ return a[0].a").equals("11"); + code_v2_("class A { a = 10 } var a = [new A()]; a[0].a-- return a[0].a").equals("9"); + code_v2_("class A { a = 10 } var a = [new A()]; ++a[0].a return a[0].a").equals("11"); + code_v2_("class A { a = 10 } var a = [new A()]; --a[0].a return a[0].a").equals("9"); + code_v2_("class A { a = 10 m() { return 12 } } var a = new A(); return a.m()").equals("12"); + code_v2_("class A { a = 10 m() { return 13 } } var a = new A(); return a['m']()").equals("13"); + code_v2_("class A { a = 10 m() { return 13 } } var a = new A(); var m = 'm' return a[m]()").equals("13"); + code_v2_("class A { a = 10 m() { return a } } var a = new A(); var array = [a.m] return array[0](a)").equals("10"); + code_v2_("class A { a = 10 m() { return a } } var a = new A(); var array = [a['m']] return array[0](a)").equals("10"); + + section("Reserved fields"); + code_v2("class A { for while if var this }").error(Error.NONE); + code_v3_("class A { for }").error(Error.VARIABLE_NAME_UNAVAILABLE); + code_v3_("class A { if }").error(Error.VARIABLE_NAME_UNAVAILABLE); + code_v3_("class A { while }").error(Error.VARIABLE_NAME_UNAVAILABLE); + code_v3_("class A { var }").error(Error.VARIABLE_NAME_UNAVAILABLE); + code_v3_("class A { this }").error(Error.VARIABLE_NAME_UNAVAILABLE); + + section("Constructors"); + code_v2_("class A {} var a = new A() return a").equals("A {}"); + code_v2_("class A { constructor() {} } var a = new A() return a").equals("A {}"); + code_v2_("class A { constructor() { return; } } var a = new A() return a").equals("A {}"); + + section("Static fields"); + code_v2_("class A { static x }").equals("null"); + code_v2_("class A { static x } return A.x").equals("null"); + code_v2_("class A { static x = 10 } return A.x").equals("10"); + code_v2_("class A { static x = 'hello' } return A.x").equals("hello"); + code_v2_("class A { static x = [1, 2, 3] } return A.x").equals("[1, 2, 3]"); + code_v2_("class A { static x = null } return A.x").equals("null"); + code_v2_("class Affiche { static COULEUR = getColor(42, 125, 78) } return Affiche.COULEUR").equals("2784590"); + code_v2_("class A { static b } A.c").error(Error.CLASS_STATIC_MEMBER_DOES_NOT_EXIST); + code_v2_("class A { static b static m() { class.c } }").error(Error.CLASS_STATIC_MEMBER_DOES_NOT_EXIST); + + section("Reserved static fields"); + code_v2("class A { static for static while static if static var static this }").error(Error.NONE); + code_v3_("class A { static for }").error(Error.VARIABLE_NAME_UNAVAILABLE); + code_v3_("class A { static if }").error(Error.VARIABLE_NAME_UNAVAILABLE); + code_v3_("class A { static while }").error(Error.VARIABLE_NAME_UNAVAILABLE); + code_v3_("class A { static var }").error(Error.VARIABLE_NAME_UNAVAILABLE); + code_v3_("class A { static this }").error(Error.VARIABLE_NAME_UNAVAILABLE); + + section("Operators on field"); + code_v2_("class A { a = 10 } var a = new A(); return --a.a").equals("9"); + code_v2_("class A { a = 10 } var a = new A(); a.a-- return a.a").equals("9"); + code_v2_("class A { a = 10 } var a = new A(); return ++a.a").equals("11"); + code_v2_("class A { a = 10 } var a = new A(); a.a++ return a.a").equals("11"); + code_v2_("class A { a = 10 } var a = new A(); return a.a += 5").equals("15"); + code_v2_("class A { a = 10 } var a = new A(); return a.a -= 5").equals("5"); + code_v2_("class A { a = 10 } var a = new A(); return a.a *= 5").equals("50"); + code_v2_("class A { a = 10 } var a = new A(); return a.a /= 5").equals("2.0"); + code_v2_("class A { a = 10 } var a = new A(); return a.a %= 5").equals("0"); + code_v2_("class A { a = 10 } var a = new A(); return a.a **= 5").equals("100000"); + code_v2_("class A { a = 10 } var a = new A(); return a.a |= 5").equals("15"); + code_v2_("class A { a = 10 } var a = new A(); return a.a &= 5").equals("0"); + code_v2_("class A { a = 10 } var a = new A(); return a.a ^= 5").equals("15"); + code_v2_("class A { a = 10 } var a = new A(); return a.a <<= 5").equals("320"); + code_v2_("class A { a = 10 } var a = new A(); return a.a >>= 5").equals("0"); + code_v2_("class A { a = 10 } var a = new A(); return a.a >>>= 5").equals("0"); + + section("Operators on field in method"); + code_v2_("class A { a = 10 m() { return --a } } return new A().m()").equals("9"); + code_v2_("class A { a = 10 m() { a-- return a } } return new A().m()").equals("9"); + code_v2_("class A { a = 10 m() { return ++a } } return new A().m()").equals("11"); + code_v2_("class A { a = 10 m() { a++ return a } } return new A().m()").equals("11"); + code_v2_("class A { a = 10 m() { return a += 5 } } return new A().m()").equals("15"); + code_v2_("class A { a = 10 m() { return a -= 5 } } return new A().m()").equals("5"); + code_v2_("class A { a = 10 m() { return a *= 5 } } return new A().m()").equals("50"); + code_v2_("class A { a = 10 m() { return a /= 5 } } return new A().m()").equals("2.0"); + code_v2_("class A { a = 10 m() { return a %= 5 } } return new A().m()").equals("0"); + code_v2_("class A { a = 10 m() { return a **= 5 } } return new A().m()").equals("100000"); + code_v2_("class A { a = 10 m() { return a |= 5 } } return new A().m()").equals("15"); + code_v2_("class A { a = 10 m() { return a &= 5 } } return new A().m()").equals("0"); + code_v2_("class A { a = 10 m() { return a ^= 5 } } return new A().m()").equals("15"); + code_v2_("class A { a = 10 m() { return a <<= 5 } } return new A().m()").equals("320"); + code_v2_("class A { a = 10 m() { return a >>= 5 } } return new A().m()").equals("0"); + code_v2_("class A { a = 10 m() { return a >>>= 5 } } return new A().m()").equals("0"); + + section("Operators on static field"); + code_v2_("class A { static a = 10 } return --A.a").equals("9"); + code_v2_("class A { static a = 10 } A.a-- return A.a").equals("9"); + code_v2_("class A { static a = 10 } return ++A.a").equals("11"); + code_v2_("class A { static a = 10 } A.a++ return A.a").equals("11"); + code_v2_("class A { static a = 10 } return A.a += 5").equals("15"); + code_v2_("class A { static a = 10 } return A.a -= 5").equals("5"); + code_v2_("class A { static a = 10 } return A.a *= 5").equals("50"); + code_v2_("class A { static a = 10 } return A.a /= 5").equals("2.0"); + code_v2_("class A { static a = 10 } return A.a %= 5").equals("0"); + code_v2_("class A { static a = 10 } return A.a **= 5").equals("100000"); + code_v2_("class A { static a = 10 } return A.a |= 5").equals("15"); + code_v2_("class A { static a = 10 } return A.a &= 5").equals("0"); + code_v2_("class A { static a = 10 } return A.a ^= 5").equals("15"); + code_v2_("class A { static a = 10 } return A.a <<= 5").equals("320"); + code_v2_("class A { static a = 10 } return A.a >>= 5").equals("0"); + code_v2_("class A { static a = 10 } return A.a >>>= 5").equals("0"); + + section("Operators on static field in method"); + code_v2_("class A { static a = 10 static m() { return --a } } return A.m()").equals("9"); + code_v2_("class A { static a = 10 static m() { a-- return a } } return A.m()").equals("9"); + code_v2_("class A { static a = 10 static m() { return ++a } } return A.m()").equals("11"); + code_v2_("class A { static a = 10 static m() { a++ return a } } return A.m()").equals("11"); + code_v2_("class A { static a = 10 static m() { return a += 5 } } return A.m()").equals("15"); + code_v2_("class A { static a = 10 static m() { return a -= 5 } } return A.m()").equals("5"); + code_v2_("class A { static a = 10 static m() { return a *= 5 } } return A.m()").equals("50"); + code_v2_("class A { static a = 10 static m() { return a /= 5 } } return A.m()").equals("2.0"); + code_v2_("class A { static a = 10 static m() { return a %= 5 } } return A.m()").equals("0"); + code_v2_("class A { static a = 10 static m() { return a **= 5 } } return A.m()").equals("100000"); + code_v2_("class A { static a = 10 static m() { return a |= 5 } } return A.m()").equals("15"); + code_v2_("class A { static a = 10 static m() { return a &= 5 } } return A.m()").equals("0"); + code_v2_("class A { static a = 10 static m() { return a ^= 5 } } return A.m()").equals("15"); + code_v2_("class A { static a = 10 static m() { return a <<= 5 } } return A.m()").equals("320"); + code_v2_("class A { static a = 10 static m() { return a >>= 5 } } return A.m()").equals("0"); + code_v2_("class A { static a = 10 static m() { return a >>>= 5 } } return A.m()").equals("0"); + + section("Operators on static field in method with class."); + code_v2_("class A { static a = 10 static m() { return --class.a } } return A.m()").equals("9"); + code_v2_("class A { static a = 10 static m() { class.a-- return class.a } } return A.m()").equals("9"); + code_v2_("class A { static a = 10 static m() { return ++class.a } } return A.m()").equals("11"); + code_v2_("class A { static a = 10 static m() { class.a++ return class.a } } return A.m()").equals("11"); + code_v2_("class A { static a = 10 static m() { return class.a += 5 } } return A.m()").equals("15"); + code_v2_("class A { static a = 10 static m() { return class.a -= 5 } } return A.m()").equals("5"); + code_v2_("class A { static a = 10 static m() { return class.a *= 5 } } return A.m()").equals("50"); + code_v2_("class A { static a = 10 static m() { return class.a /= 5 } } return A.m()").equals("2.0"); + code_v2_("class A { static a = 10 static m() { return class.a %= 5 } } return A.m()").equals("0"); + code_v2_("class A { static a = 10 static m() { return class.a **= 5 } } return A.m()").equals("100000"); + code_v2_("class A { static a = 10 static m() { return class.a |= 5 } } return A.m()").equals("15"); + code_v2_("class A { static a = 10 static m() { return class.a &= 5 } } return A.m()").equals("0"); + code_v2_("class A { static a = 10 static m() { return class.a ^= 5 } } return A.m()").equals("15"); + code_v2_("class A { static a = 10 static m() { return class.a <<= 5 } } return A.m()").equals("320"); + code_v2_("class A { static a = 10 static m() { return class.a >>= 5 } } return A.m()").equals("0"); + code_v2_("class A { static a = 10 static m() { return class.a >>>= 5 } } return A.m()").equals("0"); + + section("Static methods"); + code_v2_("class A { static a() { return 12 } } return A.a()").equals("12"); + code_v2_("class A { static a(x) { return 12 } } return A.a()").error(Error.INVALID_PARAMETER_COUNT); + code_v2_("class A { static a() { return 12 } } return A.b()").error(Error.CLASS_STATIC_MEMBER_DOES_NOT_EXIST); + code_v2_("class A { static f(x) {} static g() { f(1) } }").error(Error.NONE); + code_v2_("class A { static f(x) {} static g() { f() } }").error(Error.INVALID_PARAMETER_COUNT); + code_v2_("class A { static f(x) {} static g() { class.f() } }").error(Error.INVALID_PARAMETER_COUNT); + + section("Static method calls with with class."); + code_v2_("class A { static m() { return 'x' } t() { return class.m() } } var a = new A() return a.t()").equals("x"); + code_v2_("class A { static m() { return 'x' } t() { return class.zz() } } var a = new A() return a.t()").error(Error.CLASS_STATIC_MEMBER_DOES_NOT_EXIST); + + section("Methods"); + code_v2_("class A { a(x) { b(x) } b(x, y) {} }").error(Error.INVALID_PARAMETER_COUNT); + code_v2_("class A { a(x) { this.b(x) } b(x, y) {} }").error(Error.INVALID_PARAMETER_COUNT); + + section("Field access by array access"); + code_v2_("var test = {} test['a'] = 8 return test").equals("{a: 8}"); + code_v2_("var test = {} test['a'] = 8 test['b'] = 12 return test").equals("{a: 8, b: 12}"); + code_v2_("class Test { a b c } var test2 = new Test() test2['a'] = 8 return test2").equals("Test {a: 8, b: null, c: null}"); + code_v2_("class Test { a b c } var test2 = new Test() for (var field in Test.fields) { test2[field] = 8 } return test2").equals("Test {a: 8, b: 8, c: 8}"); + code_v2_("class Test { a b c constructor(a, b, c) { this.a = a this.b = b this.c = c } } var test1 = new Test(1, 2, 3) var test2 = new Test() for (var field in test1.class.fields) { test2[field] = test1[field] } return test2").equals("Test {a: 1, b: 2, c: 3}"); + code_v2_("class Test { a b c constructor(a, b, c) { this.a = a this.b = b this.c = c } } var test1 = new Test(1, 2, 3) var test2 = new Test() for (var field in test2.class.fields) { test2[field] = test1[field] } return test2").equals("Test {a: 1, b: 2, c: 3}"); + code_v2_("class A { a = 6 m() { return this['a'] } } return new A().m()").equals("6"); + + section("Operators on field by array access"); + code_v2_("class A { a = 10 } var a = new A(); return --a['a']").equals("9"); + code_v2_("class A { a = 10 } var a = new A(); a['a']-- return a['a']").equals("9"); + code_v2_("class A { a = 10 } var a = new A(); return ++a['a']").equals("11"); + code_v2_("class A { a = 10 } var a = new A(); a['a']++ return a['a']").equals("11"); + code_v2_("class A { a = 10 } var a = new A(); return a['a'] += 5").equals("15"); + code_v2_("class A { a = 10 } var a = new A(); return a['a'] -= 5").equals("5"); + code_v2_("class A { a = 10 } var a = new A(); return a['a'] *= 5").equals("50"); + code_v2_("class A { a = 10 } var a = new A(); return a['a'] /= 5").equals("2.0"); + code_v2_("class A { a = 10 } var a = new A(); return a['a'] %= 5").equals("0"); + code_v2_("class A { a = 10 } var a = new A(); return a['a'] **= 5").equals("100000"); + code_v2_("class A { a = 10 } var a = new A(); return a['a'] |= 5").equals("15"); + code_v2_("class A { a = 10 } var a = new A(); return a['a'] &= 5").equals("0"); + code_v2_("class A { a = 10 } var a = new A(); return a['a'] ^= 5").equals("15"); + code_v2_("class A { a = 10 } var a = new A(); return a['a'] <<= 5").equals("320"); + code_v2_("class A { a = 10 } var a = new A(); return a['a'] >>= 5").equals("0"); + code_v2_("class A { a = 10 } var a = new A(); return a['a'] >>>= 5").equals("0"); + + section("Static field access by array access"); + code_v2_("class Test { static a static b static c } Test['a'] = 12 Test['b'] = 15 Test['c'] = 20 return [Test.a, Test.b, Test.c]").equals("[12, 15, 20]"); + code_v2_("class A { static a = 6 static m() { return class['a'] } } return A.m()").equals("6"); + + section("Operators on static field by array access"); + code_v2_("class A { static a = 10 } return --A['a']").equals("9"); + code_v2_("class A { static a = 10 } A['a']-- return A['a']").equals("9"); + code_v2_("class A { static a = 10 } return ++A['a']").equals("11"); + code_v2_("class A { static a = 10 } A['a']++ return A['a']").equals("11"); + code_v2_("class A { static a = 10 } return A['a'] += 5").equals("15"); + code_v2_("class A { static a = 10 } return A['a'] -= 5").equals("5"); + code_v2_("class A { static a = 10 } return A['a'] *= 5").equals("50"); + code_v2_("class A { static a = 10 } return A['a'] /= 5").equals("2.0"); + code_v2_("class A { static a = 10 } return A['a'] %= 5").equals("0"); + code_v2_("class A { static a = 10 } return A['a'] **= 5").equals("100000"); + code_v2_("class A { static a = 10 } return A['a'] |= 5").equals("15"); + code_v2_("class A { static a = 10 } return A['a'] &= 5").equals("0"); + code_v2_("class A { static a = 10 } return A['a'] ^= 5").equals("15"); + code_v2_("class A { static a = 10 } return A['a'] <<= 5").equals("320"); + code_v2_("class A { static a = 10 } return A['a'] >>= 5").equals("0"); + code_v2_("class A { static a = 10 } return A['a'] >>>= 5").equals("0"); + + section("Assign this/class/super"); + code_v2_("class A { m() { this = 12 } }").error(Error.CANT_ASSIGN_VALUE); + code_v2_("class A { m() { class = 12 } }").error(Error.CANT_ASSIGN_VALUE); + code_v2_("class B {} class A extends B { m() { super = 12 } }").error(Error.CANT_ASSIGN_VALUE); + + section("Inheritance"); + code_v2_("class A { x = 10 } class B extends A {} var a = new B() return a.x").equals("10"); + code_v2_("class A { m() { return 'ok' } } class B extends A { m() { return super.m() } } var a = new B() return a.m()").equals("ok"); + code_v2_("class A { x = 10 } class B extends A {} class C extends B {} var a = new C() return a.x").equals("10"); + code_v2_("class A { m() { return 'ok' } } class B extends A {} class C extends B {} var a = new C() return a.m()").equals("ok"); + code_v2_("class A { m() { return 'ok' } } class B extends A { m() { return super.m() }} class C extends B { m() { return super.m() } } var a = new C() return a.m()").equals("ok"); + code_v2_("class A { m() { return 'ok' } } class B extends A {} class C extends B { m() { return super.m() } } var a = new C() return a.m()").equals("ok"); + code_v2_("class A { m() { return 'okA' } } class B extends A { m() { return super.m() + 'B' }} class C extends B { m() { return super.m() + 'C' } } var a = new C()return a.m()").equals("okABC"); + code_v2_("class A { items } class B extends A { constructor() { this.items = [] } } var x = new B() return x").equals("B {items: []}"); + code_v2_("class A { items } class B extends A { constructor() { this.items = [] super() } } var x = new B() return x").equals("B {items: []}"); + code_v2_("class A { m() { return 'parent' } t() { return this.m() } } class B extends A { m() { return 'enfant' } } return new B().t()").equals("enfant"); + code_v2_("class A { m() { return 'parent' } t() { return m() } } class B extends A { m() { return 'enfant' } } return new B().t()").equals("enfant"); + code_v2_("class A { public id; } class W extends A {} class H extends W { constructor(id){ this.id=id } }").equals("null"); + code_v2_("class A { public x(a) { return a } } class B extends A { public x(a, b) { return x(a + b) } } return new B().x(5, 7)").equals("12"); + + section("Access levels: fields"); + code_v2_("class A { x = 10 } var a = new A() return a.x").equals("10"); + code_v2_("class A { public x = 10 } var a = new A() return a.x").equals("10"); + code_v2_("class A { protected x = 10 } var a = new A() return a.x").equals("null"); + code_v2_("class A { private x = 10 } var a = new A() return a.x").equals("null"); + code_v2_("class A { private x = 10 m() { return x } } var a = new A() return a.m()").equals("10"); + code_v2_("class A { private x = 10 } class B extends A {} var a = new B() return a.x").equals("null"); + code_v2_("class A { protected x = 10 } class B extends A {} var a = new B() return a.x").equals("null"); + code_v2_("class A { protected x = 10 } class B extends A { m() { return x } } var a = new B() return a.m()").equals("10"); + code_v2_("class A { private x = 10 constructor() { x = 15 } } var a = new A() return a").equals("A {x: 15}"); + code_v2_("class A { private x; constructor() { this.x = []; } } return new A()").equals("A {x: []}"); + code_v2_("class Parent { private chaine = 'Nawak'; public get_chaine_parent() { return this.chaine; } } class Enfant extends Parent { public get_chaine_enfant() { return this.get_chaine_parent() } } var e = Enfant() return [e.get_chaine_parent(), e.get_chaine_enfant() ]").equals("[Nawak, Nawak]"); + code_v2_("class Parent { protected chaine = 'Nawak'; public get_chaine_parent() { return this.chaine; } } class Enfant extends Parent { public get_chaine_enfant() { return this.get_chaine_parent() } } var e = Enfant() return [e.get_chaine_parent(), e.get_chaine_enfant() ]").equals("[Nawak, Nawak]"); + code_v2_("class A { private x; constructor() { this.x = [] push(this.x, 10); } } return new A()").equals("A {x: [10]}"); + + code_v2_("class A { private x = 10 m() { return x } } var a = new A() return a.m()").equals("10"); + section("Access levels: static fields"); + code_v2_("class A { static x = 10 } return A.x").equals("10"); + code_v2_("class A { public static x = 10 } return A.x").equals("10"); + code_v2_("class A { protected static x = 10 } return A.x").equals("null"); + code_v2_("class A { private static x = 10 } return A.x").equals("null"); + code_v2_("class A { private static x = 10 static m() { return x } } return A.m()").equals("10"); + code_v2_("class A { private static x = 10 } class B extends A {} return B.x").equals("null"); + code_v2_("class A { protected static x = 10 } class B extends A {} return B.x").equals("null"); + code_v2_("class A { protected static x = 10 } class B extends A { static m() { return x } } return B.m()").equals("10"); + code_v2_("class A { private static x = 10 static m() { return A.x } } return A.m()").equals("10"); + + section("Access levels: methods"); + code_v2_("class A { m() { return 10 } } var a = new A() return a.m()").equals("10"); + code_v2_("class A { public m() { return 10 } } var a = new A() return a.m()").equals("10"); + code_v2_("class A { protected m() { return 10 } } var a = new A() return a.m()").equals("null"); + code_v2_("class A { private m() { return 10 } } var a = new A() return a.m()").equals("null"); + code_v2_("class A { public m() { return 10 } } class B extends A {} var a = new B() return a.m()").equals("10"); + code_v2_("class A { protected m() { return 10 } } class B extends A {} var a = new B() return a.m()").equals("null"); + code_v2_("class A { private m() { return 10 } } class B extends A {} var a = new B() return a.m()").equals("null"); + code_v2_("class A { protected m() { return 10 } } class B extends A { m() { return super.m() } } var a = new B() return a.m()").equals("10"); + + section("Access levels: constructors"); + code_v2_("class A { constructor() { } } return new A()").equals("A {}"); + code_v2_("class A { public constructor() { } } return new A()").equals("A {}"); + code_v2_("class A { protected constructor() { } } return new A()").error(Error.PROTECTED_CONSTRUCTOR); + code_v2_("class A { private constructor() { } } return new A()").error(Error.PRIVATE_CONSTRUCTOR); + code_v2_("class A { public constructor() { } } class B extends A {} return new B()").equals("B {}"); + code_v2_("class A { x protected constructor() { x = 10 } } class B extends A { constructor() { super() } } return new B().x").equals("10"); + code_v2_("class A { x private constructor() { x = 10 } } class B extends A { constructor() { super() } } return new B().x").error(Error.PRIVATE_CONSTRUCTOR); + code_v2_("class A { private constructor() { } } class B extends A {} return new B()").equals("B {}"); + code_v2_("class A { private constructor() {} static getInstance() { return new A() } } return A.getInstance()").equals("A {}"); + + section("Inheritance fields"); + code_v2_("class A { a = 7 } class B extends A { } return new B().a").equals("7"); + code_v2_("class A { a = 7 } class B extends A { } return new B()['a']").equals("7"); + code_v2_("class A { a = 7 } class B extends A { } return new B()['a']++").equals("7"); + + section("Inheritance static fields"); + code_v2_("class A { static a = 7 } class B extends A { } return B.a").equals("7"); + code_v2_("class A { static a = 7 } class B extends A { } return B['a']").equals("7"); + code_v2_("class A { static a = 7 } class B extends A { } return B['a']++").equals("7"); + + section("Constructor as function"); + code_v2_("class A { x constructor(x) { this.x = x } } var f = A var o = {c: f} return o.c('a')").equals("A {x: a}"); + code_v2_("class A { x constructor(x) { this.x = x } } var a = [1, 2, 3, 4] return arrayMap(a, A)").equals("[A {x: 1}, A {x: 2}, A {x: 3}, A {x: 4}]"); + + section("Access levels: static methods"); + code_v2_("class A { static m() { return 10 } } return A.m()").equals("10"); + code_v2_("class A { public static m() { return 10 } } return A.m()").equals("10"); + code_v2_("class A { protected static m() { return 10 } } return A.m()").error(Error.PROTECTED_STATIC_METHOD); + code_v2_("class A { private static m() { return 10 } } return A.m()").error(Error.PRIVATE_STATIC_METHOD); + code_v2_("class A { public static m() { return 10 } } class B extends A {} return B.m()").equals("10"); + code_v2_("class A { protected static m() { return 10 } } class B extends A {} return B.m()").error(Error.PROTECTED_STATIC_METHOD); + code_v2_("class A { private static m() { return 10 } } class B extends A {} return B.m()").error(Error.PRIVATE_STATIC_METHOD); + + section("Initialization of fields"); + code_v2_("class A { x = [1, 2, 3] } var a = new A() return a.x").equals("[1, 2, 3]"); + code_v2_("class A { x = [1, 2, 3] } var a = new A() push(a.x, 4) var b = new A() return b.x").equals("[1, 2, 3]"); + code_v2_("class B { y = 10 } class A { x = new B() } var a = new A() return a.x").equals("B {y: 10}"); + code_v2_("class B { y = 10 } class A { x = new B() } var a = new A() return a.x.y").equals("10"); + code_v2_("class B { y = 10 } class A { static x = new B() } return A.x").equals("B {y: 10}"); + + section("Initialization of static fields"); + code_v2_("class A { public static x = arrayMap([1, 3, 5], function(y) { return y ** 3 }) } return A.x").equals("[1, 27, 125]"); + code_v2_("class A { static x = arrayMap([1, 3, 5], function(y) { return y ** 3 }) } return A.x").equals("[1, 27, 125]"); + code_v2_("class Map { public static obstacles = toUpper('hello') } return Map.obstacles").equals("HELLO"); + + section("Method is a system method"); + code_v2_("class A { sqrt() { return sqrt(25) } }").equals("null"); + code_v2("return sqrt(25, 12)").error(Error.NONE); + code_v3_("return sqrt(25, 12)").error(Error.INVALID_PARAMETER_COUNT); + code_v2_("class A {} return new A().sqrt()").equals("null"); + code_v2_("class A { sqrt() { return sqrt(25) } } return new A().sqrt()").equals("5.0"); + code_v2_("class A { sqrt() { return sqrt(10, 15) } sqrt(x, y) { return sqrt(x + y) } } return new A().sqrt()").equals("5.0"); + + section("Static method is a system method"); + code_v2_("class A { static sqrt() { return sqrt(25) } }").equals("null"); + code_v2_("class A {} return new A().sqrt()").equals("null"); + code_v2_("class A { static sqrt() { return sqrt(25) } } return A.sqrt()").equals("5.0"); + code_v2_("class A { static sqrt() { return sqrt(10, 15) } static sqrt(x, y) { return sqrt(x + y) } } return A.sqrt()").equals("5.0"); + + section("Method as value"); + code_v2_("class A { x = 12 m() { return x } } var o = new A() var r = [A.m] return r[0](o)").equals("12"); + code_v2_("class A { m() { return 12 } } var o = new A() var r = {x: A.m} return r.x(o)").equals("12"); + code_v2_("class A { m() { return 12 } } var o = new A() var r = [A.m] var m = r[0] return m(o)").equals("12"); + code_v2_("class A { m() { return [1, 2, 3] } } var o = new A() var r = [A.m] var m = r[0] return m(o)").equals("[1, 2, 3]"); + code_v2_("class A { m(x, y) { return x * y } } class B { x = A.m } return new B().x(new A(), 5, 12)").equals("60"); + code_v2_("class A { m(x, y) { return x * y } } var f = A.m return f(new A(), 5, 12)").equals("60"); + code_v2_("class A { m(x, y) { return x * y } } var f = new A().m return f(new A(), 5, 12)").equals("60"); + code_v2_("class A { m(x, y) { return x * y } } var f = A.m return f(new A(), 5)").equals("null"); + + section("Static method as value"); + code_v2_("class A { static m() { return 12 } } var r = [A.m] return r[0]()").equals("12"); + code_v2_("class A { static m() { return 12 } } var r = {x: A.m} return r.x()").equals("12"); + code_v2_("class A { static m() { return 12 } } var r = [A.m] var m = r[0] return m()").equals("12"); + code_v2_("class A { static m() { return [1, 2, 3] } } var r = [A.m] var m = r[0] return m()").equals("[1, 2, 3]"); + code_v2_("class a { static method() { return '42' } } class b { toto constructor() { this.toto = a.method } } return new b().toto()").equals("42"); + code_v2_("class a { static method() { return '42' } } class b { toto constructor() { this.toto = a.method } } var o = new b() return o.toto()").equals("42"); + code_v2_("class a { static method() { return '42' } } class b { toto constructor() { this.toto = a.method } m() { return this.toto() } } var o = new b() return o.m()").equals("42"); + code_v2_("class Test { private static method_1() { return 4 } private static method_2() { return 9 } public static array = [1: Test.method_1, 2: Test.method_2] } return [Test.array[1](), Test.array[2]()]").equals("[4, 9]"); + code_v2_("class Test { private static method_1() { return 4 } private static method_2() { return 9 } public static array = [1: Test.method_1, 2: Test.method_2] } return arrayMap(Test.array, function(x) { return x() })").equals("[1 : 4, 2 : 9]"); + code_v2_("class A { a a() { return 12 } } return new A().a()").equals("12"); + + section("Return of field"); + code_v2_("class R { f = [] m(k, r) { return this.f[k] = r } } var x = new R() return x.m(1, 2)").equals("2"); + code_v2_("class R { private f = [] m(k, r) { return this.f[k] = r } } var x = new R() return x.m(1, 'hello')").equals("hello"); + + section("Constant in static field"); + code_v2_("class A { static bulbsNameChip = ['puny_bulb': PI] } return A.bulbsNameChip").equals("[puny_bulb : 3.141592653589793]"); + + section("Misc"); + code_v2_("class A { static x() {} static m(item) { return x == item } } return A.m(12)").equals("false"); + code_v2_("class A { static x() {} static m(item) { return x == item } } return A.m(A.x)").equals("true"); + code_v2_("class A { static f(x) { x = 2 var g = function() { f(x) } } }").equals("null"); + code_v2_("class A { f(x) { x = 2 var g = function() { f(x) } } }").equals("null"); + code_v2_("class A { f(x) {} constructor(x) { x = 2 var g = function() { f(x) } } }").equals("null"); + + section("Base classes"); + code_v3_("return Value").equals(""); + code_v3_("return Null").equals(""); + code_v3_("return Number").equals(""); + code_v3_("return Integer").equals(""); + code_v3_("return Real").equals(""); + code_v3_("return String").equals(""); + code_v3_("return Array").equals(""); + code_v3_("return Object").equals(""); + code_v3_("return Function").equals(""); + code_v3_("return Class").equals(""); + code_v3_("return JSON").equals(""); + code_v3_("return System").equals(""); + + code_v2("return Array").error(Error.UNKNOWN_VARIABLE_OR_FUNCTION); + code_v2("class Array {} return Array").equals(""); + + section(".class"); + code_v2("return null.class").equals("null"); + code_v3_("return null.class").equals(""); + code_v2("return true.class").equals("null"); + code_v3_("return true.class").equals(""); + code_v2("return (12).class").equals("null"); + code_v3_("return (12).class").equals(""); + code_v2("return (12.5).class").equals("null"); + code_v3_("return (12.5).class").equals(""); + code_v2("return 'salut'.class").equals("null"); + code_v3_("return 'salut'.class").equals(""); + code_v2("return [].class").equals("null"); + code_v3_("return [].class").equals(""); + code_v2_("return {}.class").equals(""); + code_v2("return (function() {}).class").equals("null"); + code_v3_("return (function() {}).class").equals(""); + code_v2("class A {} return A.class").equals("null"); + code_v3_("class A {} return A.class").equals(""); + code_v2_("class A {} return new A().class").equals(""); + + section("Class.class"); + code_v2_("class A { class }").error(Error.RESERVED_FIELD); + code_v2_("class A { class() {} }").error(Error.RESERVED_FIELD); + code_v2_("class A { } return new A().class").equals(""); + code_v2("class A { } return A.class").equals("null"); + code_v3_("class A { } return A.class").equals(""); + + section("Class.super"); + code_v2_("class A { super }").error(Error.RESERVED_FIELD); + code_v2_("class A { super() {} }").error(Error.RESERVED_FIELD); + code_v2_("class A { } class B extends A {} return B.super").equals(""); + code_v2_("class A { } class B extends A {} return B.super.name").equals("A"); + code_v2_("class A { } class B extends A {} return new B().class.super.name").equals("A"); + code_v2("class A { } return A.class.super").equals("null"); + code_v3_("class A { } return A.class.super").equals(""); + + section("Class.name"); + code_v2_("class A { static name }").error(Error.RESERVED_FIELD); + code_v2_("class A { static name() {} }").error(Error.NONE); + code_v2_("class A {} return A.name").equals("A"); + code_v2_("class A {} return new A().class.name").equals("A"); + + section("Class.fields"); + code_v2_("class A { static fields }").error(Error.RESERVED_FIELD); + code_v2_("class A { } return A.fields").equals("[]"); + code_v2_("class A { x y z } return A.fields").equals("[x, y, z]"); + code_v2_("class A { z y x } return A.fields").equals("[z, y, x]"); + code_v2_("class A { a b c static d m() {} n() {} o() {} } return A.fields").equals("[a, b, c]"); + + section("Class.staticFields"); + code_v2_("class A { static staticFields }").error(Error.RESERVED_FIELD); + code_v2_("class A { } return A.staticFields").equals("[]"); + code_v2_("class A { static x static y static z } return A.staticFields").equals("[x, y, z]"); + code_v2_("class A { static z static y static x } return A.staticFields").equals("[z, y, x]"); + code_v2_("class A { static a static b c d m() {} n() {} o() {} } return A.staticFields").equals("[a, b]"); + + section("Class.methods"); + code_v2_("class A { static methods }").error(Error.RESERVED_FIELD); + code_v2_("class A { a() {} b() {} static c() {} } return A.methods").equals("[a, b]"); + + section("Class.staticMethods"); + code_v2_("class A { static staticMethods }").error(Error.RESERVED_FIELD); + code_v2_("class A { a() {} b() {} static c() {} static d() {} } return A.staticMethods").equals("[c, d]"); + + section("Object.string()"); + code_v2_("return string({x: 1, y: 2, z: 3})").equals("{x: 1, y: 2, z: 3}"); + code_v2_("return string({z: 1, y: 2, x: 3})").equals("{z: 1, y: 2, x: 3}"); + code_v2_("class A { z = 1 y = 2 x = 3 } return string(new A())").equals("A {z: 1, y: 2, x: 3}"); + code_v2_("class A { x = 1 y = 2 z = 3 } return string(new A())").equals("A {x: 1, y: 2, z: 3}"); + + /* + * Operators + */ + section("Object.operator !"); + code_v2_("return !{}").equals("true"); + code_v2_("return !{a: 32}").equals("false"); + + section("Object.operator | |"); + // code_v11("var a = {a: 32, b: 'toto', c: false}; return |a|").equals("3"); + + section("Object.operator in ()"); + // code_v11("return 12 in {x: 5, y: 12}").equals("true"); + // code_v11("return 12 in {x: 5, y: 'yo'}").equals("false"); + + section("Object.operator . ()"); + code_v2_("return { v: 12 }.v").equals("12"); + code_v2_("var a = {b: 12, c: 5} return a.b").equals("12"); + code_v2_("var a = {v: 5} return a.v = 12").equals("12"); + code_v2_("var a = {v: 5} a.v = 12 return a").equals("{v: 12}"); + code_v2_("var a = {v: 5} return a.v = 'salut'").equals("salut"); + code_v2_("var a = {v: 5} a.v = 'salut' return a").equals("{v: salut}"); + code_v2_("var a = {b: 12} return a.b += 10").equals("22"); + code_v2_("var a = {b: 12} return a.b -= 10").equals("2"); + code_v2_("var a = {b: 12} return a.b *= 10").equals("120"); + code_v2_("var a = {b: 12} return a.b /= 10").almost(1.2); + code_v2_("var a = {b: 12} return a.b %= 10").equals("2"); + code_v2_("var o = {} o.new_val = 12 return o").equals("{new_val: 12}"); + code_v2_("var o = {a: 'a'} o.b = 'b' return o").equals("{a: a, b: b}"); + // DISABLED_code("Object.readonly.v = 5").exception(ls::vm::Exception::CANT_MODIFY_READONLY_OBJECT); + // code_v11("var o = [{}, ''][0] return o.values").equals(""); + code_v2_("var pq = [{p: 22, v: 55}] return pq[0].p").equals("22"); + code_v2_("var pq = [{p: 22, v: 55}] var o = pq[0] return o.v").equals("55"); + + section("Object.operator =="); + code_v2_("class A {} return {} == new A").equals("false"); + code_v2_("class A { }; class B { }; return new A() == new B();").equals("false"); + code_v2_("class A {} return new A == new A").equals("false"); + code_v2_("return {a: 2} == {}").equals("false"); + code_v2_("return {a: 2} == {a: 1}").equals("false"); + code_v2_("return {a: 2} == {b: 2}").equals("false"); + code_v2_("return {a: 2} == {a: 2}").equals("false"); + + section("Object.operator <"); + // code("return {} < {}").equals("false"); + // code("return {a: 2} < {a: 3}").equals("true"); + // code("return {a: 2} < {a: 1}").equals("false"); + // code("return {a: 'b'} < {a: 'c'}").equals("true"); + // code("return {a: 'b'} < {a: 'a'}").equals("false"); + // code("return {b: 2} < {c: 2}").equals("true"); + // code("return {b: 2} < {a: 2}").equals("false"); + // code("return {a: 1} < {a: 1, b: 2}").equals("true"); + // code("return {a: 1, b: 2} < {a: 1}").equals("false"); + // code("return {a: 0, b: 2} < {a: 1}").equals("true"); + // code("class A {} class B {} return new A < new B").equals("true"); + // code("class A {} class B {} return new B < new A").equals("false"); + // code("class A {} return {} < new A").equals("true"); + // code("class A {} return new A < {}").equals("false"); + + section("Operator instanceof"); + code_v3_("return null instanceof Null").equals("true"); + code_v3_("return true instanceof Boolean").equals("true"); + code_v3_("return false instanceof Boolean").equals("true"); + code_v3_("return 12 instanceof Integer").equals("true"); + code_v3_("return 12 instanceof Real").equals("true"); + code_v3_("return 12.5 instanceof Integer").equals("false"); + code_v3_("return 'hello' instanceof String").equals("true"); + code_v3_("return [] instanceof Array").equals("true"); + code_v3_("return {} instanceof Object").equals("true"); + code_v3_("return Number instanceof Class").equals("true"); + code_v3_("return function() {} instanceof Function").equals("true"); + + /* + * Iteration + */ + // code("var s = '' for v in {a: 5, b: 'hello'} { s += v } s").error(ls::Error::Type::VALUE_NOT_ITERABLE, {"{a: 5, b: 'hello'}", env.tmp_object->to_string()}); // TODO .equals("'5hello'"); + + /* + * Methods + */ + section("Object.keys()"); + code_v3_("return {}.keys()").equals("[]"); + code_v3_("return {a: 5, b: 'toto', c: true, d: function() { return 5 } }.keys()").equals("[a, b, c, d]"); + code_v3_("class A { x y z } return new A().keys()").equals("[x, y, z]"); + code_v3_("class A { z y x } return new A().keys()").equals("[z, y, x]"); + // code_v2_("return 'x' in {x: 5, y: 'yo'}.keys()").equals("true"); + // code_v2_("return 'x' in {a: 5, y: 'yo'}.keys()").equals("false"); + + section("Object.values()"); + code_v3_("return {}.values()").equals("[]"); + code_v3_("return {a: 1}.values()").equals("[1]"); + code_v3_("return {a: 1, b: 1}.values()").equals("[1, 1]"); + code_v3_("return {a: 5, b: 'toto', c: true, d: function() { return 5 } }.values()").equals("[5, toto, true, #Anonymous Function]"); + code_v3_("return {c: 5, a: 'toto', d: true, b: function() { return 5 } }.values()").equals("[5, toto, true, #Anonymous Function]"); + + section("Object.isTrue()"); + code_v2_("if ({x: 12}) { return 5 } else { return 12 }").equals("5"); + code_v2_("if ({}) { return 5 } else { return 12 }").equals("12"); + + section("Object.clone()"); + code_v2_("var a = {v: 12} return [a]").equals("[{v: 12}]"); + + section("Object.map()"); + // code("return {}.map(x -> x + 1)").equals("{}"); + // code("return {x: 12, y: 5}.map(x -> x + 1)").equals("{x: 13, y: 6}"); + // code("return {x: 'a', y: 'b'}.map(x -> x + ' !')").equals("{x: 'a !', y: 'b !'}"); + } +} diff --git a/src/test/java/test/TestOperators.java b/src/test/java/test/TestOperators.java index 27b4ddf8..9851229f 100644 --- a/src/test/java/test/TestOperators.java +++ b/src/test/java/test/TestOperators.java @@ -1,182 +1,214 @@ package test; -import static org.junit.Assert.fail; - -import org.junit.Before; - -import leekscript.runner.AI; -import leekscript.runner.LeekFunctions; -import leekscript.runner.LeekOperations; -import leekscript.runner.values.AbstractLeekValue; -import leekscript.runner.values.ArrayLeekValue; -import leekscript.runner.values.BooleanLeekValue; -import leekscript.runner.values.DoubleLeekValue; -import leekscript.runner.values.FunctionLeekValue; -import leekscript.runner.values.IntLeekValue; -import leekscript.runner.values.NullLeekValue; -import leekscript.runner.values.StringLeekValue; - -import org.junit.Test; - -public class TestOperators { - - AI ai; - - @Before - public void init() throws Exception { - ai = new TestAI(); - } - - @Test - public void simpleEqualsOperators() throws Exception { - try { - AI uai = new TestAI(); - if (!LeekOperations.equals(uai, new StringLeekValue("Chaine1"), new StringLeekValue("Chaine1")).getBoolean()) - fail("Chaine1 != Chaine1"); - if (LeekOperations.equals(uai, new StringLeekValue("Chaine1"), new StringLeekValue("Chaine2")).getBoolean()) - fail("Chaine1 == Chaine2"); - if (!LeekOperations.equals(uai, new BooleanLeekValue(false), new BooleanLeekValue(false)).getBoolean()) - fail("false != false"); - if (!LeekOperations.equals(uai, new BooleanLeekValue(true), new BooleanLeekValue(true)).getBoolean()) - fail("true != true"); - if (LeekOperations.equals(uai, new BooleanLeekValue(false), new BooleanLeekValue(true)).getBoolean()) - fail("false == true"); - if (LeekOperations.equals(uai, new BooleanLeekValue(true), new BooleanLeekValue(false)).getBoolean()) - fail("true == false"); - if (LeekOperations.equals(uai, new IntLeekValue(1), new IntLeekValue(2)).getBoolean()) - fail("1 == 2"); - if (LeekOperations.equals(uai, new IntLeekValue(-1), new IntLeekValue(-5)).getBoolean()) - fail("-1 == -5"); - if (!LeekOperations.equals(uai, new IntLeekValue(50), new IntLeekValue(50)).getBoolean()) - fail("50 != 50"); - if (!LeekOperations.equals(uai, new IntLeekValue(0), new IntLeekValue(0)).getBoolean()) - fail("0 != 0"); - if (!LeekOperations.equals(uai, new DoubleLeekValue(0), new DoubleLeekValue(0)).getBoolean()) - fail("0.0 != 0.0"); - if (!LeekOperations.equals(uai, new DoubleLeekValue(5), new DoubleLeekValue(5)).getBoolean()) - fail("5.0 != 5.0"); - if (LeekOperations.equals(uai, new DoubleLeekValue(45), new DoubleLeekValue(5)).getBoolean()) - fail("45.0 == 5.0"); - if (!LeekOperations.equals(uai, new NullLeekValue(), new NullLeekValue()).getBoolean()) - fail("null != null"); - if (!LeekOperations.equals(uai, new ArrayLeekValue(), new ArrayLeekValue()).getBoolean()) - fail("[] != []"); - if (!LeekOperations.equals(uai, new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(0) }), - new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(0) })) - .getBoolean()) - fail("[0] != [0]"); - if (!LeekOperations.equals(uai, new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(0), new IntLeekValue(1) }), - new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(0), new IntLeekValue(1) })).getBoolean()) - fail("[0,1] != [0,1]"); - if (LeekOperations.equals(uai, new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(0), new IntLeekValue(1) }), - new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(0) })).getBoolean()) - fail("[0,1] == [0]"); - - if (LeekOperations.equals(uai, new ArrayLeekValue(ai, new AbstractLeekValue[] { new StringLeekValue("Chaine1") }), - new ArrayLeekValue(ai, new AbstractLeekValue[] { new StringLeekValue("Chaine2") })).getBoolean()) - fail("[\"Chaine1\"] == [\"Chaine2\"]"); - if (!LeekOperations.equals(uai, new ArrayLeekValue(ai, new AbstractLeekValue[] { new StringLeekValue("Chaine1") }), - new ArrayLeekValue(ai, new AbstractLeekValue[] { new StringLeekValue("Chaine1") })).getBoolean()) - fail("[\"Chaine1\"] != [\"Chaine1\"]"); - - if (!LeekOperations.equals(uai, new FunctionLeekValue(1), new FunctionLeekValue(1)).getBoolean()) - fail("#F1 != #F1"); - if (LeekOperations.equals(uai, new FunctionLeekValue(1), new FunctionLeekValue(2)).getBoolean()) - fail("#F1 == #AF1"); - if (LeekOperations.equals(uai, new FunctionLeekValue(LeekFunctions.endsWith), new FunctionLeekValue(1)).getBoolean()) - fail("#endsWith == #AF1"); - if (!LeekOperations.equals(uai, new FunctionLeekValue(LeekFunctions.endsWith), new FunctionLeekValue(LeekFunctions.endsWith)).getBoolean()) - fail("#endsWith != #endsWith"); - } catch (Exception e) { - fail(e.getMessage()); +import java.util.ArrayList; +import java.util.List; +import leekscript.common.Error; + +public class TestOperators extends TestCommon { + + public void run() throws Exception { + + section("Operator =="); + code("return null == null").equals("true"); + + code("return false == false").equals("true"); + code("return true == true").equals("true"); + code("return false == true").equals("false"); + code("return true == false").equals("false"); + code("return true == 'true'").equals("true"); + code("return false == 'false'").equals("true"); + code("return true == 'false'").equals("false"); + code("return false == 'true'").equals("false"); + code("return false == []").equals("true"); + code("return false == 0").equals("true"); + code("return true == 1").equals("true"); + code("return false != 0").equals("false"); + code("return true != 1").equals("false"); + code("return false == ''").equals("true"); + code("return false == '0'").equals("true"); + code("return false == []").equals("true"); + code("return false == [0]").equals("true"); + code("return true == 12").equals("true"); + code("return true == '1'").equals("true"); + code("return true == '12'").equals("true"); + code("return true == 'lama'").equals("true"); + code("return true == [1]").equals("true"); + code("return true == [12]").equals("true"); + code("return true == [1, 2, 3]").equals("true"); + + code("return 0 == false").equals("true"); + code("return 0 == 0").equals("true"); + code("return 0 == ''").equals("true"); + code("return 0 == '0'").equals("true"); + code("return 0 == 'false'").equals("true"); + code("return 0 == []").equals("true"); + code("return 0 == [0]").equals("true"); + code("return 0 != null").equals("true"); + + code("return 1 == true").equals("true"); + code("return 1 == '1'").equals("true"); + code("return 1 == 'true'").equals("true"); + code("return 1 == 'lama'").equals("false"); + code("return 1 == [1]").equals("true"); + code("return 1 == 2").equals("false"); + + code("return 12 == true").equals("true"); + code("return -1 == -5").equals("false"); + code("return 50 == 50").equals("true"); + code("return 5 == 5").equals("true"); + code("return 45 == 5").equals("false"); + code("return 10 == '10'").equals("true"); + code("return 10 == '10'").equals("true"); + code("return 10 == '15'").equals("false"); + code("return 10 == '15'").equals("false"); + code("return 10.8 == '10.8'").equals("true"); + code("return 10.8 == '10.87'").equals("false"); + code("return 12 == 'true'").equals("true"); + code("return 2 == 'false'").equals("false"); + code("return 12 == [12]").equals("true"); + + code("return 'Chaine1' == 'Chaine1'").equals("true"); + code("return 'Chaine1' == 'Chaine2'").equals("false"); + code("return '1' == 1").equals("true"); + code("return '0' == 0").equals("true"); + code("return '10' == 10").equals("true"); + code("return '15' == 10").equals("false"); + + code("return [] == []").equals("true"); + code("return [0] == [0]").equals("true"); + code("return [0, 1] == [0, 1]").equals("true"); + code("return [0, 1] == [0]").equals("false"); + code("return ['Chaine1'] == ['Chaine2']").equals("false"); + code("return ['Chaine1'] == ['Chaine1']").equals("true"); + + code("return function() {} == function() {}").equals("false"); + code("return endsWith == function() {}").equals("false"); + code("return endsWith == endsWith").equals("true"); + + String[] values1 = new String[] { "false", "true", "0", "1", "12", "''", "'0'", "'1'", "'12'", "'lama'", "'true'", "'false'", + "[]", "[0]", "[1]", "[12]", "[1,2,3]", "null" }; + String[] equalEqual = new String[] { + "X X XX XXX ", // false + " X XX XXXX XXX ", // true + "X X XX XXX ", // 0 + " X X X X X ", // 1 + " X X X X X ", // 12 + "X X X XX ", // " " + "X X X X ", // "0" + " X X X X ", // "1" + " X X X X ", // "12" + " X X ", // "lama" + " X XX X XXX ", // "true" + "X X XXX ", // "false" + "X X X XX ", // [] + "X X XX X X ", // [0] + " X X X X X ", // [1] + " X X X X X ", // [12] + " X X X ", // [1,2,3] + " X", // null + }; + + for (int i = 0; i < values1.length; i++) { + for (int j = 0; j < values1.length; j++) { + code("return " + values1[i] + " == " + values1[j]).equals(String.valueOf(equalEqual[i].charAt(j) == 'X')); + } } - } - @Test - public void advancedEqualsOperators() { - try { - // String-Intger - AI uai = new TestAI(); - if (!LeekOperations.equals(uai, new StringLeekValue("1"), new IntLeekValue(1)).getBoolean()) - fail("\"1\" != 1"); - if (!LeekOperations.equals(uai, new StringLeekValue("0"), new IntLeekValue(0)).getBoolean()) - fail("\"0\" != 0"); - if (!LeekOperations.equals(uai, new StringLeekValue("10"), new IntLeekValue(10)).getBoolean()) - fail("\"10\" != 10"); - if (LeekOperations.equals(uai, new StringLeekValue("15"), new IntLeekValue(10)).getBoolean()) - fail("\"15\" == 10"); - if (!LeekOperations.equals(uai, new IntLeekValue(1), new StringLeekValue("1")).getBoolean()) - fail("1 != \"1\""); - if (!LeekOperations.equals(uai, new IntLeekValue(0), new StringLeekValue("0")).getBoolean()) - fail("0 != \"0\""); - if (!LeekOperations.equals(uai, new DoubleLeekValue(0), new StringLeekValue("0")).getBoolean()) - fail("0.0 != \"0\""); - if (!LeekOperations.equals(uai, new IntLeekValue(10), new StringLeekValue("10")).getBoolean()) - fail("10 != \"10\""); - if (!LeekOperations.equals(uai, new DoubleLeekValue(10), new StringLeekValue("10")).getBoolean()) - fail("10.0 != \"10\""); - if (LeekOperations.equals(uai, new IntLeekValue(10), new StringLeekValue("15")).getBoolean()) - fail("10 == \"15\""); - if (LeekOperations.equals(uai, new DoubleLeekValue(10), new StringLeekValue("15")).getBoolean()) - fail("10.0 == \"15\""); - if (!LeekOperations.equals(uai, new DoubleLeekValue(10.8), new StringLeekValue("10.8")).getBoolean()) - fail("10.8 != \"10.8\""); - if (LeekOperations.equals(uai, new DoubleLeekValue(10.8), new StringLeekValue("10.87")).getBoolean()) - fail("10.8 == \"10.87\""); - - if (!LeekOperations.equals(uai, new BooleanLeekValue(true), new StringLeekValue("true")).getBoolean()) - fail("true != \"true\""); - if (!LeekOperations.equals(uai, new BooleanLeekValue(false), new StringLeekValue("false")).getBoolean()) - fail("false != \"false\""); - if (LeekOperations.equals(uai, new BooleanLeekValue(true), new StringLeekValue("false")).getBoolean()) - fail("true == \"false\""); - if (LeekOperations.equals(uai, new BooleanLeekValue(false), new StringLeekValue("true")).getBoolean()) - fail("false == \"true\""); - - if (!LeekOperations.equals(uai, new IntLeekValue(1), new StringLeekValue("true")).getBoolean()) - fail("1 != \"true\""); - if (!LeekOperations.equals(uai, new IntLeekValue(0), new StringLeekValue("false")).getBoolean()) - fail("0 != \"false\""); - if (!LeekOperations.equals(uai, new IntLeekValue(12), new StringLeekValue("true")).getBoolean()) - fail("12 != \"true\""); - if (LeekOperations.equals(uai, new IntLeekValue(2), new StringLeekValue("false")).getBoolean()) - fail("2 == \"false\""); - - if (!LeekOperations.equals(uai, new IntLeekValue(0), new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(0) })).getBoolean()) - fail("0 != [0]"); - if (!LeekOperations.equals(uai, new IntLeekValue(1), new ArrayLeekValue(ai, new AbstractLeekValue[] { new IntLeekValue(1) })).getBoolean()) - fail("1 != [1]"); - if (!LeekOperations.equals(uai, new IntLeekValue(0), new ArrayLeekValue()).getBoolean()) - fail("0 != []"); - if (!LeekOperations.equals(uai, new BooleanLeekValue(false), new ArrayLeekValue()).getBoolean()) - fail("0 != []"); - } catch (Exception e) { - fail(e.getMessage()); + section("Other operators"); + code("var sum = 1, ops = 10 return sum < ops * 0.95 || sum > ops").equals("true"); + code("var sum = 9.8, ops = 10 return sum < ops * 0.95 || sum > ops").equals("false"); + code("var sum = 98 var ops = 100 return sum < ops * 0.95 || sum > ops").equals("false"); + code("var sum = 98 var ops = 100 if (sum < ops * 0.95 || sum > ops) {}").equals("null"); + code("var sum = 1 var ops = 10 if (sum < ops * 0.95 || sum > ops) {} return null").equals("null"); + code("var a = [] if (a != []) {}").equals("null"); + code("return !null == 50").equals("true"); + + section("Operator ==="); + Object[] values = new Object[] { "0", "1", + "12", "13", "false", + "true", "null", "'true'", + "'false'", "'12'", + "'lama'", + "[]", "['12']" }; + + for (int i = 0; i < values.length; i++) { + for (int j = 0; j < values.length; j++) { + code("return " + values[i] + " === " + values[j]).equals(String.valueOf(i == j)); + } } - } - - @Test - public void tripleEqualsTest() throws Exception { - - AbstractLeekValue[] values = new AbstractLeekValue[] { new IntLeekValue(0), new IntLeekValue(1), - new IntLeekValue(12), new DoubleLeekValue(13), new BooleanLeekValue(false), - new BooleanLeekValue(true), new NullLeekValue(), new StringLeekValue("true"), - new StringLeekValue("false"), new StringLeekValue("12"), - new StringLeekValue("lama"), - new ArrayLeekValue(), new ArrayLeekValue(ai, new AbstractLeekValue[] { - new StringLeekValue("12") }) }; - - int i, j; - for (i = 0; i < values.length; i++) { - for (j = 0; j < values.length; j++) { - if (LeekOperations.equals_equals(ai, values[i], values[j]).getBoolean() != (i == j)) - fail(values[i].getString(ai) + ((i == j) ? "!==" : "===") + values[j].getString(ai)); + code("return 1 === 1.0").equals("true"); + code("return 12 === 12.0").equals("true"); + code("return null == [null]").equals("false"); + code("return null != [null]").equals("true"); + code_v1("return [null] == null").equals("true"); // Bug in LS1.0 + code_v2_("return [null] == null").equals("false"); // Fixed in 1.1 + code_v1("return [null] != null").equals("false"); // Bug in LS1.0 + code_v2_("return [null] != null").equals("true"); // Fixed in 1.1 + + code("var a = 1; var result = -10 + (1- (a-1)); return result").equals("-9"); + code("var a = 1; var result = 0; result = -10 + (1- (a-1)); return result").equals("-9"); + + code("return null < 3").equals("true"); + code("var a = null return a < 3").equals("true"); + code("return true < 10").equals("true"); + code("return false < 10").equals("true"); + code("return 10 < true").equals("false"); + code("return 10 < false").equals("false"); + code("return 10 > true").equals("true"); + code("return 10 > false").equals("true"); + code("return true > 10").equals("false"); + code("return false > 10").equals("false"); + + code("var a = 20 if (15 > a > 11) { return true } return false").equals("false"); + code("return 15 > 14 > 11 and 150 < 200 < 250").equals("false"); + code("return 15 > 10 > 11 and 150 < 200 < 250").equals("false"); + code("return 15 > 14 > 11 and 150 < 100 < 250").equals("false"); + code("return 15 > 10 > 11 and 150 < 100 < 250").equals("false"); + + section("Operator +"); + code("return false + 1").equals("1"); + code("return 1 + false").equals("1"); + code("return true + 1").equals("2"); + code("return 1 + true").equals("2"); + code("return true + null").equals("1"); + code("return null + true").equals("1"); + code("return false + null").equals("0"); + code("return null + false").equals("0"); + + section("Assignment operators"); + Object[] values2 = new Object[] { + "null", "true", "false", "12", "5.678", "false", "true", "'lama'", "[1, 2, 3, 4, 5]" + }; + Object[] operators = new Object[] { + "+", "-", "*", "/", "%", "&", "^", "|", "<<", ">>", ">>>", "**", + }; + Object[] assignmentOperators = new Object[] { + "+=", "-=", "*=", "/=", "%=", "&=", "^=", "|=", "<<=", ">>=", ">>>=", "**=", + }; + String[] actualResults = new String[] { + "0", "0", "0", "null", "null", "0", "0", "0", "0", "0", "0", "1", "0", "0", "0", "null", "null", "0", "1", "0", "0", "0", "0", "1", "1", "-1", "0", "0", "0", "0", "1", "1", "0", "0", "0", "0", "1", "-1", "0", "0", "0", "0", "0", "1", "0", "0", "0", "0", "0", "0", "0", "null", "null", "0", "0", "0", "0", "0", "0", "1", "0", "0", "0", "null", "null", "0", "1", "0", "0", "0", "0", "1", "12", "-12", "0", "0", "0", "0", "12", "12", "0", "0", "0", "0", "12", "-12", "0", "0", "0", "0", "0", "12", "0", "0", "0", "0", "5,678", "-5,678", "0", "0", "0", "0", "5", "5", "0", "0", "0", "0", "5,678", "-5,678", "0", "0", "0", "0", "0", "5", "0", "0", "0", "0", "0", "0", "0", "null", "null", "0", "0", "0", "0", "0", "0", "1", "0", "0", "0", "null", "null", "0", "1", "0", "0", "0", "0", "1", "1", "-1", "0", "0", "0", "0", "1", "1", "0", "0", "0", "0", "1", "-1", "0", "0", "0", "0", "0", "1", "0", "0", "0", "0", "nulllama", "-4", "0", "0", "0", "0", "4", "4", "0", "0", "0", "0", "nulllama", "-4", "0", "0", "0", "0", "0", "4", "0", "0", "0", "0", "[null, 1, 2, 3, 4, 5]", "-5", "0", "0", "0", "0", "5", "5", "0", "0", "0", "0", "[null, 1, 2, 3, 4, 5]", "-5", "0", "0", "0", "0", "0", "5", "0", "0", "0", "0", "1", "1", "0", "null", "null", "0", "1", "1", "1", "1", "1", "1", "1", "1", "0", "null", "null", "0", "1", "1", "1", "1", "1", "1", "2", "0", "1", "1", "0", "1", "0", "1", "2", "0", "0", "1", "2", "0", "1", "1", "0", "1", "1", "1", "2", "0", "0", "1", "1", "1", "0", "null", "null", "0", "1", "1", "1", "1", "1", "1", "1", "1", "0", "null", "null", "0", "1", "1", "1", "1", "1", "1", "13", "-11", "12", "0,083", "1", "0", "13", "13", "4096", "0", "0", "1", "13", "-11", "12", "0,083", "1", "0", "1", "13", "4096", "0", "0", "1", "6,678", "-4,678", "5,678", "0,176", "1", "1", "4", "5", "32", "0", "0", "1", "6,678", "-4,678", "5,678", "0,176", "1", "1", "1", "5", "32", "0", "0", "1", "1", "1", "0", "null", "null", "0", "1", "1", "1", "1", "1", "1", "1", "1", "0", "null", "null", "0", "1", "1", "1", "1", "1", "1", "2", "0", "1", "1", "0", "1", "0", "1", "2", "0", "0", "1", "2", "0", "1", "1", "0", "1", "1", "1", "2", "0", "0", "1", "truelama", "-3", "4", "0,25", "1", "0", "5", "5", "16", "0", "0", "1", "truelama", "-3", "4", "0,25", "1", "0", "1", "5", "16", "0", "0", "1", "[true, 1, 2, 3, 4, 5]", "-4", "5", "0,2", "1", "1", "4", "5", "32", "0", "0", "1", "[true, 1, 2, 3, 4, 5]", "-4", "5", "0,2", "1", "1", "1", "5", "32", "0", "0", "1", "0", "0", "0", "null", "null", "0", "0", "0", "0", "0", "0", "1", "0", "0", "0", "null", "null", "0", "1", "0", "0", "0", "0", "1", "1", "-1", "0", "0", "0", "0", "1", "1", "0", "0", "0", "0", "1", "-1", "0", "0", "0", "0", "0", "1", "0", "0", "0", "0", "0", "0", "0", "null", "null", "0", "0", "0", "0", "0", "0", "1", "0", "0", "0", "null", "null", "0", "1", "0", "0", "0", "0", "1", "12", "-12", "0", "0", "0", "0", "12", "12", "0", "0", "0", "0", "12", "-12", "0", "0", "0", "0", "0", "12", "0", "0", "0", "0", "5,678", "-5,678", "0", "0", "0", "0", "5", "5", "0", "0", "0", "0", "5,678", "-5,678", "0", "0", "0", "0", "0", "5", "0", "0", "0", "0", "0", "0", "0", "null", "null", "0", "0", "0", "0", "0", "0", "1", "0", "0", "0", "null", "null", "0", "1", "0", "0", "0", "0", "1", "1", "-1", "0", "0", "0", "0", "1", "1", "0", "0", "0", "0", "1", "-1", "0", "0", "0", "0", "0", "1", "0", "0", "0", "0", "falselama", "-4", "0", "0", "0", "0", "4", "4", "0", "0", "0", "0", "falselama", "-4", "0", "0", "0", "0", "0", "4", "0", "0", "0", "0", "[false, 1, 2, 3, 4, 5]", "-5", "0", "0", "0", "0", "5", "5", "0", "0", "0", "0", "[false, 1, 2, 3, 4, 5]", "-5", "0", "0", "0", "0", "0", "5", "0", "0", "0", "0", "12", "12", "0", "null", "null", "0", "12", "12", "12", "12", "12", "1", "12", "12", "0", "null", "null", "0", "1", "12", "12", "12", "12", "1", "13", "11", "12", "12", "0", "0", "13", "13", "24", "6", "6", "12", "13", "11", "12", "12", "0", "0", "12", "13", "24", "6", "6", "12", "12", "12", "0", "null", "null", "0", "12", "12", "12", "12", "12", "1", "12", "12", "0", "null", "null", "0", "1", "12", "12", "12", "12", "1", "24", "0", "144", "1", "0", "12", "0", "12", "49152", "0", "0", "2147483647", "24", "0", "144", "1", "0", "12", "2147483647", "12", "49152", "0", "0", "2147483647", "17,678", "6,322", "68,136", "2,113", "0,644", "4", "9", "13", "384", "0", "0", "1 341 501,353", "17,678", "6,322", "68,136", "2,113", "0,644", "4", "1 341 501,353", "13", "384", "0", "0", "1 341 501,353", "12", "12", "0", "null", "null", "0", "12", "12", "12", "12", "12", "1", "12", "12", "0", "null", "null", "0", "1", "12", "12", "12", "12", "1", "13", "11", "12", "12", "0", "0", "13", "13", "24", "6", "6", "12", "13", "11", "12", "12", "0", "0", "12", "13", "24", "6", "6", "12", "12lama", "8", "48", "3", "0", "4", "8", "12", "192", "0", "0", "20736", "12lama", "8", "48", "3", "0", "4", "20736", "12", "192", "0", "0", "20736", "[12, 1, 2, 3, 4, 5]", "7", "60", "2,4", "2", "4", "9", "13", "384", "0", "0", "248832", "[12, 1, 2, 3, 4, 5]", "7", "60", "2,4", "2", "4", "248832", "13", "384", "0", "0", "248832", "5,678", "5,678", "0", "null", "NaN", "0", "5", "5", "5", "5", "5", "1", "5,678", "5,678", "0", "null", "NaN", "0", "1", "5", "5", "5", "5", "1", "6,678", "4,678", "5,678", "5,678", "0,678", "1", "4", "5", "10", "2", "2", "5,678", "6,678", "4,678", "5,678", "5,678", "0,678", "1", "5,678", "5", "10", "2", "2", "5,678", "5,678", "5,678", "0", "null", "NaN", "0", "5", "5", "5", "5", "5", "1", "5,678", "5,678", "0", "null", "NaN", "0", "1", "5", "5", "5", "5", "1", "17,678", "-6,322", "68,136", "0,473", "5,678", "4", "9", "13", "20480", "0", "0", "1 122 909 247,194", "17,678", "-6,322", "68,136", "0,473", "5,678", "4", "1 122 909 247,194", "13", "20480", "0", "0", "1 122 909 247,194", "11,356", "0", "32,24", "1", "0", "5", "0", "5", "160", "0", "0", "19 156,732", "11,356", "0", "32,24", "1", "0", "5", "19 156,732", "5", "160", "0", "0", "19 156,732", "5,678", "5,678", "0", "null", "NaN", "0", "5", "5", "5", "5", "5", "1", "5,678", "5,678", "0", "null", "NaN", "0", "1", "5", "5", "5", "5", "1", "6,678", "4,678", "5,678", "5,678", "0,678", "1", "4", "5", "10", "2", "2", "5,678", "6,678", "4,678", "5,678", "5,678", "0,678", "1", "5,678", "5", "10", "2", "2", "5,678", "5,678lama", "1,678", "22,712", "1,419", "1,678", "4", "1", "5", "80", "0", "0", "1 039,397", "5,678lama", "1,678", "22,712", "1,419", "1,678", "4", "1 039,397", "5", "80", "0", "0", "1 039,397", "[5,678, 1, 2, 3, 4, 5]", "0,678", "28,39", "1,136", "0,678", "5", "0", "5", "160", "0", "0", "5 901,697", "[5,678, 1, 2, 3, 4, 5]", "0,678", "28,39", "1,136", "0,678", "5", "5 901,697", "5", "160", "0", "0", "5 901,697", "0", "0", "0", "null", "null", "0", "0", "0", "0", "0", "0", "1", "0", "0", "0", "null", "null", "0", "1", "0", "0", "0", "0", "1", "1", "-1", "0", "0", "0", "0", "1", "1", "0", "0", "0", "0", "1", "-1", "0", "0", "0", "0", "0", "1", "0", "0", "0", "0", "0", "0", "0", "null", "null", "0", "0", "0", "0", "0", "0", "1", "0", "0", "0", "null", "null", "0", "1", "0", "0", "0", "0", "1", "12", "-12", "0", "0", "0", "0", "12", "12", "0", "0", "0", "0", "12", "-12", "0", "0", "0", "0", "0", "12", "0", "0", "0", "0", "5,678", "-5,678", "0", "0", "0", "0", "5", "5", "0", "0", "0", "0", "5,678", "-5,678", "0", "0", "0", "0", "0", "5", "0", "0", "0", "0", "0", "0", "0", "null", "null", "0", "0", "0", "0", "0", "0", "1", "0", "0", "0", "null", "null", "0", "1", "0", "0", "0", "0", "1", "1", "-1", "0", "0", "0", "0", "1", "1", "0", "0", "0", "0", "1", "-1", "0", "0", "0", "0", "0", "1", "0", "0", "0", "0", "falselama", "-4", "0", "0", "0", "0", "4", "4", "0", "0", "0", "0", "falselama", "-4", "0", "0", "0", "0", "0", "4", "0", "0", "0", "0", "[false, 1, 2, 3, 4, 5]", "-5", "0", "0", "0", "0", "5", "5", "0", "0", "0", "0", "[false, 1, 2, 3, 4, 5]", "-5", "0", "0", "0", "0", "0", "5", "0", "0", "0", "0", "1", "1", "0", "null", "null", "0", "1", "1", "1", "1", "1", "1", "1", "1", "0", "null", "null", "0", "1", "1", "1", "1", "1", "1", "2", "0", "1", "1", "0", "1", "0", "1", "2", "0", "0", "1", "2", "0", "1", "1", "0", "1", "1", "1", "2", "0", "0", "1", "1", "1", "0", "null", "null", "0", "1", "1", "1", "1", "1", "1", "1", "1", "0", "null", "null", "0", "1", "1", "1", "1", "1", "1", "13", "-11", "12", "0,083", "1", "0", "13", "13", "4096", "0", "0", "1", "13", "-11", "12", "0,083", "1", "0", "1", "13", "4096", "0", "0", "1", "6,678", "-4,678", "5,678", "0,176", "1", "1", "4", "5", "32", "0", "0", "1", "6,678", "-4,678", "5,678", "0,176", "1", "1", "1", "5", "32", "0", "0", "1", "1", "1", "0", "null", "null", "0", "1", "1", "1", "1", "1", "1", "1", "1", "0", "null", "null", "0", "1", "1", "1", "1", "1", "1", "2", "0", "1", "1", "0", "1", "0", "1", "2", "0", "0", "1", "2", "0", "1", "1", "0", "1", "1", "1", "2", "0", "0", "1", "truelama", "-3", "4", "0,25", "1", "0", "5", "5", "16", "0", "0", "1", "truelama", "-3", "4", "0,25", "1", "0", "1", "5", "16", "0", "0", "1", "[true, 1, 2, 3, 4, 5]", "-4", "5", "0,2", "1", "1", "4", "5", "32", "0", "0", "1", "[true, 1, 2, 3, 4, 5]", "-4", "5", "0,2", "1", "1", "1", "5", "32", "0", "0", "1", "lamanull", "4", "0", "null", "null", "0", "4", "4", "4", "4", "4", "1", "lamanull", "4", "0", "null", "null", "0", "1", "4", "4", "4", "4", "1", "lamatrue", "3", "4", "4", "0", "0", "5", "5", "8", "2", "2", "4", "lamatrue", "3", "4", "4", "0", "0", "4", "5", "8", "2", "2", "4", "lamafalse", "4", "0", "null", "null", "0", "4", "4", "4", "4", "4", "1", "lamafalse", "4", "0", "null", "null", "0", "1", "4", "4", "4", "4", "1", "lama12", "-8", "48", "0,333", "4", "4", "8", "12", "16384", "0", "0", "16777216", "lama12", "-8", "48", "0,333", "4", "4", "16777216", "12", "16384", "0", "0", "16777216", "lama5,678", "-1,678", "22,712", "0,704", "4", "4", "1", "5", "128", "0", "0", "2 621,179", "lama5,678", "-1,678", "22,712", "0,704", "4", "4", "2 621,179", "5", "128", "0", "0", "2 621,179", "lamafalse", "4", "0", "null", "null", "0", "4", "4", "4", "4", "4", "1", "lamafalse", "4", "0", "null", "null", "0", "1", "4", "4", "4", "4", "1", "lamatrue", "3", "4", "4", "0", "0", "5", "5", "8", "2", "2", "4", "lamatrue", "3", "4", "4", "0", "0", "4", "5", "8", "2", "2", "4", "lamalama", "0", "16", "1", "0", "4", "0", "4", "64", "0", "0", "256", "lamalama", "0", "16", "1", "0", "4", "256", "4", "64", "0", "0", "256", "lama[1, 2, 3, 4, 5]", "-1", "20", "0,8", "4", "4", "1", "5", "128", "0", "0", "1024", "lama[1, 2, 3, 4, 5]", "-1", "20", "0,8", "4", "4", "1024", "5", "128", "0", "0", "1024", "[1, 2, 3, 4, 5, null]", "5", "0", "null", "null", "0", "5", "5", "5", "5", "5", "1", "[1, 2, 3, 4, 5, null]", "5", "0", "null", "null", "0", "1", "5", "5", "5", "5", "1", "[1, 2, 3, 4, 5, true]", "4", "5", "5", "0", "1", "4", "5", "10", "2", "2", "5", "[1, 2, 3, 4, 5, true]", "4", "5", "5", "0", "1", "5", "5", "10", "2", "2", "5", "[1, 2, 3, 4, 5, false]", "5", "0", "null", "null", "0", "5", "5", "5", "5", "5", "1", "[1, 2, 3, 4, 5, false]", "5", "0", "null", "null", "0", "1", "5", "5", "5", "5", "1", "[1, 2, 3, 4, 5, 12]", "-7", "60", "0,417", "5", "4", "9", "13", "20480", "0", "0", "244140625", "[1, 2, 3, 4, 5, 12]", "-7", "60", "0,417", "5", "4", "244140625", "13", "20480", "0", "0", "244140625", "[1, 2, 3, 4, 5, 5,678]", "-0,678", "28,39", "0,881", "5", "5", "0", "5", "160", "0", "0", "9 305,757", "[1, 2, 3, 4, 5, 5,678]", "-0,678", "28,39", "0,881", "5", "5", "9 305,757", "5", "160", "0", "0", "9 305,757", "[1, 2, 3, 4, 5, false]", "5", "0", "null", "null", "0", "5", "5", "5", "5", "5", "1", "[1, 2, 3, 4, 5, false]", "5", "0", "null", "null", "0", "1", "5", "5", "5", "5", "1", "[1, 2, 3, 4, 5, true]", "4", "5", "5", "0", "1", "4", "5", "10", "2", "2", "5", "[1, 2, 3, 4, 5, true]", "4", "5", "5", "0", "1", "5", "5", "10", "2", "2", "5", "[1, 2, 3, 4, 5]lama", "1", "20", "1,25", "1", "4", "1", "5", "80", "0", "0", "625", "[1, 2, 3, 4, 5]lama", "1", "20", "1,25", "1", "4", "625", "5", "80", "0", "0", "625", "[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]", "0", "25", "1", "0", "5", "0", "5", "160", "0", "0", "3125", "[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]", "0", "25", "1", "0", "5", "3125", "5", "160", "0", "0", "3125" + }; + List results = new ArrayList(); + int r = 0; + String result; + for (var value1 : values2) { + for (var value2 : values2) { + for (var operator : operators) { + String expected = actualResults[r++]; + var c = code_v1("var a = " + value1 + " return a " + operator + " " + value2); + if (expected.equals("error")) result = c.error(Error.INVALID_OPERATOR); + else result = c.equals(expected); + results.add("\"" + result + "\""); + } + for (var operator : assignmentOperators) { + String expected = actualResults[r++]; + var c = code_v1("var a = " + value1 + " a " + operator + " " + value2 + " return a"); + if (expected.equals("error")) result = c.error(Error.INVALID_OPERATOR); + else result = c.equals(expected); + results.add("\"" + result + "\""); + } } } - - if (!LeekOperations.equals_equals(ai, new IntLeekValue(1), new DoubleLeekValue(1)).getBoolean()) - fail("1 !== 1.0"); - if (!LeekOperations.equals_equals(ai, new DoubleLeekValue(12), new IntLeekValue(12)).getBoolean()) - fail("12.0 !== 12"); + System.out.println(results); } } diff --git a/src/test/java/test/TestReference.java b/src/test/java/test/TestReference.java new file mode 100644 index 00000000..d9970de6 --- /dev/null +++ b/src/test/java/test/TestReference.java @@ -0,0 +1,28 @@ + + +package test; + +public class TestReference extends TestCommon { + + public void run() throws Exception { + + section("Références"); + code_v1("var t = [3, 4, 5]; var a = @t[1] a++ return t;").equals("[3, 4, 5]"); + code_v1("var t = [3, 4, 5]; var a = null a = @t[1] a++ return t;").equals("[3, 4, 5]"); + code_v1("var t = 0; var f = function(a) { t = a }; f([]);").equals("null"); + code_v1("var t = 0; var f = function(a) { t = a }; var b = []; f(b);").equals("null"); + code_v1("var t = 0; var f = function(a) { t = a }; var b = []; f(b); push(t, 5);").equals("null"); + code_v1("var t = 0; var f = function(a) { t = a }; var b = []; f(b); push(t, 5); return [t, b];").equals("[[5], []]"); + code_v1("var t = 0; var f = function(a) { t = @a }; var b = []; f(b); push(t, 5); return b;").equals("[]"); + code_v1("var t = 0; var f = function(@a) { t = @a }; var b = []; f(b); push(t, 5); return b;").equals("[5]"); + code_v1("var t = 0; var f = function(a) { t; }; f(t); return 'ok';").equals("ok"); + code_v1("function ref() { var a = 2; return @a; } return 1 / ref();").equals("0,5"); + code_v1("var a = @[1, 2, 3]; var b = @a; return (@(b));").equals("[1, 2, 3]"); + code_v1("var count = count([1, 2, 3]) return count").equals("3"); + code_v1("var a = 12; var b = @a; a++; return [a, b]").equals("[13, 12]"); + code_v1("var a = 12; var b = @a; var c = @b; a++; return [a, b, c]").equals("[13, 12, 12]"); + code_v1("var a = [12]; var b = @a[0]; a[0]++; return [a, b]").equals("[[13], 12]"); + code_v1("var a = @[1, 2, 3]; var b = @a; (@(b));").equals("null"); + code_v1("var a = @[1, 2, 3]; var b = @a; return (@(b));").equals("[1, 2, 3]"); + } +} diff --git a/src/test/java/test/TestString.java b/src/test/java/test/TestString.java new file mode 100644 index 00000000..81afadda --- /dev/null +++ b/src/test/java/test/TestString.java @@ -0,0 +1,53 @@ +package test; + +public class TestString extends TestCommon { + + public void run() { + + section("String.charAt()"); + code("return charAt('bonjour', 1)").equals("o"); + + section("String.length()"); + code("return length('bonjour')").equals("7"); + + section("String.substring()"); + code("return substring('bonjour',2,3)").equals("njo"); + + section("String.replace()"); + code("return replace('bonjour','onj','pro')").equals("bproour"); + code("return replace('testtest', 'test', '{id}')").equals("{id}{id}"); + code("return replace('testtest', 'test', '$id')").equals("$id$id"); + + section("String.indexOf()"); + code("return indexOf('bonjour','o')").equals("1"); + code("return indexOf('bonjour','o',2)").equals("4"); + + section("String.split()"); + code("return split('1:2:3:4:5',':')").equals("[1, 2, 3, 4, 5]"); + code("return split('1:2:3:4:5',':',2)").equals("[1, 2:3:4:5]"); + + section("String.toLower()"); + code("return toLower('AbCDefgh')").equals("abcdefgh"); + + section("String.toUpper()"); + code("return toUpper('AbCDefgh')").equals("ABCDEFGH"); + + section("String.startsWith()"); + code("return startsWith('bonjour','bon')").equals("true"); + code("return startsWith('bonjour','jour')").equals("false"); + + section("String.endsWith()"); + code("return endsWith('bonjour','bon')").equals("false"); + code("return endsWith('bonjour','jour')").equals("true"); + + section("String.contains()"); + code("return contains('bonjour','bon')").equals("true"); + code("return contains('bonjour','jour')").equals("true"); + code("return contains('bonjour','jourr')").equals("false"); + + section("String.number()"); + code("return contains('bonjour','bon')").equals("true"); + code("return contains('bonjour','jour')").equals("true"); + code("return contains('bonjour','jourr')").equals("false"); + } +} diff --git a/src/test/java/test/TestWithFile.java b/src/test/java/test/TestWithFile.java deleted file mode 100644 index 0cdbaa20..00000000 --- a/src/test/java/test/TestWithFile.java +++ /dev/null @@ -1,61 +0,0 @@ -package test; - -import leekscript.compiler.AIFile; -import leekscript.compiler.LeekScript; -import leekscript.compiler.resolver.ResolverContext; -import leekscript.compiler.resolver.Resolver; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -public class TestWithFile { - - @Test - public void testBasicFile() throws Exception { - assertEquals("bonjour", LeekScript.runFile("test/ai/bonjour.leek")); - } - - @Test - public void testLargeFile() throws Exception { - assertEquals("cent-vingt-trois millions quatre-cent-cinquante-six-mille-sept-cent-quatre-vingt-neuf", LeekScript.runFile("test/ai/french.leek")); - } - - @Test - public void testInclude() throws Exception { - assertEquals("[a, b, KEY]", LeekScript.runFile("test/ai/array_keys.leek")); - } - - @Test - public void testIncludeMultiple() throws Exception { - assertEquals("[a, b, KEY]", LeekScript.runFile("test/ai/include_multiple.leek")); - } - - @Test - public void testCustomResolver() throws Exception { - class CustomContext extends ResolverContext {} - LeekScript.setResolver(new Resolver() { - @Override - public AIFile resolve(String path, ResolverContext context) { - return new AIFile(path, "return 'generated';", new CustomContext()); - } - }); - assertEquals("generated", LeekScript.runFile("whatever")); - LeekScript.resetResolver(); - } - - @Test - public void testSubFolder() throws Exception { - assertEquals("sub", LeekScript.runFile("test/ai/include_sub.leek")); - } - - @Test - public void testRelativePath() throws Exception { - assertEquals("cent-vingt-trois millions quatre-cent-cinquante-six-mille-sept-cent-quatre-vingt-neuf", LeekScript.runFile("test/ai/subfolder/include_parent.leek")); - } - - @Test - public void testMultipleInclude() throws Exception { - assertEquals("bonjour", LeekScript.runFile("test/ai/multiple_includes.leek")); - } -} diff --git a/src/test/resources/ai/array_keys.leek b/src/test/resources/ai/array_keys.leek new file mode 100644 index 00000000..c0205278 --- /dev/null +++ b/src/test/resources/ai/array_keys.leek @@ -0,0 +1,5 @@ +include("library.leek"); + +var array = ['a': 12, 'b': 13, 'KEY': 15]; + +return arrayKeys(array); \ No newline at end of file diff --git a/src/test/resources/ai/bonjour.leek b/src/test/resources/ai/bonjour.leek new file mode 100644 index 00000000..df3498c4 --- /dev/null +++ b/src/test/resources/ai/bonjour.leek @@ -0,0 +1 @@ +return 'bonjour'; \ No newline at end of file diff --git a/src/test/resources/ai/code/ai.leek b/src/test/resources/ai/code/ai.leek new file mode 100644 index 00000000..c33143d7 --- /dev/null +++ b/src/test/resources/ai/code/ai.leek @@ -0,0 +1,51 @@ +function isWeapon(w) { + if (w == 12) return true + return false +} +function isChip(w) { + if (w != 12) return true + return false +} +function getChipCooldown(x) { + return 0 +} +function count(array) { + return Array.size(array) +} +function inArray(array, element) { + return Array.contains(array, element) +} + +function _partition(n, si, items, added) { + + var combos = []; + for (var i = si; i < count(items); i++) { + + var item = items[i]; + var cost = item[1]; + + if (isWeapon(item[0]) && inArray(added, item)) cost--; + + if (cost > n) continue; + + if (isChip(item[0]) && getChipCooldown(item[0]) > 0 && inArray(added, item)) continue; + + added += item; + + var subs = _partition(n - cost, i, items, added); + if (count(subs) > 0) { + for (var sub in subs) { Array.push(combos, [ + [item[0], cost] + ] + sub); } + } else { + Array.push(combos, [ + [item[0], cost] + ]); + } + } + combos; +} + +var items = [12, 5, 18] + +_partition(10, 0, items, []) diff --git a/src/test/resources/ai/code/array.leek b/src/test/resources/ai/code/array.leek new file mode 100644 index 00000000..ce976968 --- /dev/null +++ b/src/test/resources/ai/code/array.leek @@ -0,0 +1,12 @@ +var a = [] +let n = 12345 + +for (var i = 0; i < n; ++i) { + a += i +} + +var b = a.filter(x -> x > 11111) +a = a.map(x -> x + 12).filter(x -> x < 11999) +a = a.map(x -> x - 9999) + +a.foldLeft(x, y -> x + y, -1000) diff --git a/src/test/resources/ai/code/assignments.leek b/src/test/resources/ai/code/assignments.leek new file mode 100644 index 00000000..28bc0a48 --- /dev/null +++ b/src/test/resources/ai/code/assignments.leek @@ -0,0 +1,19 @@ +function f() { + var y + var x = 15 + if (false) { + if (true) { + return; + } else { + y = x + } + } else { + if (true) { + y = x + } else { + y = x + } + } + return y +} +return f(); diff --git a/src/test/resources/ai/code/break_and_continue.leek b/src/test/resources/ai/code/break_and_continue.leek new file mode 100644 index 00000000..1cf2a56c --- /dev/null +++ b/src/test/resources/ai/code/break_and_continue.leek @@ -0,0 +1,25 @@ +var values = [] + +for var i = 0; i < 10; ++i { + var b = [] + for var j = 0; j < 10; ++j { + var c = [] + for var k = 1; k <= 30; ++k { c += k } + b += [c] + } + values += [b] +} + +var sum = 0 +for v in values { + for w in v { + for x in w { + if x % 2 { continue } + if x == 18 { continue 2 } + sum += x + if (sum > 2500) { break 3 } + } + } +} + +sum diff --git a/src/test/resources/ai/code/catch_else.leek b/src/test/resources/ai/code/catch_else.leek new file mode 100644 index 00000000..d65fcb03 --- /dev/null +++ b/src/test/resources/ai/code/catch_else.leek @@ -0,0 +1 @@ +[][0] !? 6 \ No newline at end of file diff --git a/src/test/resources/ai/code/class.txt b/src/test/resources/ai/code/class.txt new file mode 100644 index 00000000..2168f48a --- /dev/null +++ b/src/test/resources/ai/code/class.txt @@ -0,0 +1,31 @@ +class A extends B { + const STATIC_CLASS_CONSTANT = 12; + let constant_field + var mutable_field = 0 + constructor(a) => constant_field = a + constructor() { + mutable_field = 12 + } + update() { ... } + update2() => ... + let f = () => 'salut' // function field +} +class Operation { + let op = + + constructor(op) => this.op = op + operator () (a, b) { + return op(a, b) + } + run(a, b) => op(a, b) +} +let plus = new Operation(×) +plus(5, 6) +plus.run(5, 6) + +var a +const a +var a = [] +function() { return 'salut' } +function m() { return 'salut' } +() => 'salut' +m() => 'salut' \ No newline at end of file diff --git a/src/test/resources/ai/code/classes_multiple.leek b/src/test/resources/ai/code/classes_multiple.leek new file mode 100644 index 00000000..4c0fbf09 --- /dev/null +++ b/src/test/resources/ai/code/classes_multiple.leek @@ -0,0 +1,22 @@ +class Engine { + power = 5000 +} +class Wheel { + diameter = 45.7 +} +class Car { + // engine = new Engine() + wheels = [] +} + +var ferrari = new Car() +for (var i in [1, 2, 3, 4]) { + var w = new Wheel() + w.diameter = i * 20 + push(ferrari.wheels, w) +} + +// TODO not working +// ferrari.wheels.foldLeft((x, s) -> s + x.diameter, 0) + +return [count(ferrari.wheels), ferrari.wheels[1].diameter, ferrari.wheels[3].diameter]; diff --git a/src/test/resources/ai/code/classes_simple.leek b/src/test/resources/ai/code/classes_simple.leek new file mode 100644 index 00000000..5e0459fb --- /dev/null +++ b/src/test/resources/ai/code/classes_simple.leek @@ -0,0 +1,14 @@ +class Car { + name = "" + engine = 5000 +} + +var ferrari = Car() +var maserati = new Car() +var lamborghini = new Car() + +ferrari.name = "Ferrari" +maserati.name = "Maserati" +lamborghini.name = "Lamborghini" + +return [ferrari.name, maserati.name, lamborghini.name]; diff --git a/src/test/resources/ai/code/code.ls b/src/test/resources/ai/code/code.ls new file mode 100644 index 00000000..8ccdf20e --- /dev/null +++ b/src/test/resources/ai/code/code.ls @@ -0,0 +1,330 @@ +/* + * Commentaire large + */ + +/* Commentaire /* Imbriqué */ */ + +/* Commentaire commenté +// */ +*/ + +// Déclaration de variables + +let e +let b = 2 +let c, d = "salut" +let k, f = 12, 5 // Commentaire à la fin +let g, h, i = 12 +global super + + +// Points virgules +var aa = 2; + +// Nombres à virgule + +__debug(12.56 - 2) +12.4 +100.0001 + + +// Chaines + +let a = '', b = "" +a = 'bonjour ça va ?' +'toto' +b = "double quotes" +let cc = " 'salut', 'yeah' " + + +// Tableaux + +let a1 = [] +let a2 = [1, 2, 3] +let a3 = [3: 12, "toto": 5] +let b2 = [12, "toto", 1.1, [1, 2, 3]] +let cles = [1: 12, 3: 5, "lama": 177] + +__debug(a1) +__debug(a2) +__debug(a3) +__debug(cles) + +__debug(a2[1]) +__debug(a3['toto']) +__debug(a2[1]) +__debug(cles["lama"]) + +//__debug(b2[toto:5]) +//__debug(b2[1:12][5:6]) + +// Swap + +// a <-> b +// a <=> b + + +// Objets + +let array = [1, 2, 3] +let o = {x: 2, y: 13, z: {a: 2}} +let compound = {x: 2, other: {val: 7, tab: [1, 2, 3]}, y: 'zzz'} + +__debug(o) +__debug(compound) + +__debug(array.size) +__debug(array.0) +__debug(o.size) +__debug(o.x) +__debug(o.z.a) +__debug(compound.other.tab[1]) + +__debug(2.class) +__debug("salut".class) +__debug({x: 12}.class) +__debug([7,"a"].class) +__debug((x -> x + 5).class) + +// Conditions + +if array is not null and a is 'z' { + let c = 13 +} else { + print("yeah") +} + +if cc or cles { + print(a) +} + +if c then print(c) else !12 xor -5 end +if a then print(a) else { + let c = 12 +} +if (a != 2) { + print(a) +} + + +// Fonctions et Lambdas + +let f1 = function(x, y = 2, z = false) return 0 end + +let f2 = function(x = "salut", y, z) { + return x * x +} + +let f3 = function(x) return x end +let f4 = -> -> -> 127 +let f9 = x,y -> [y: x, x: y] +let f10 = x,y -> z -> [[x,z], [y,z], -> x+y+z] + +getLife(12) + +[1, 2, 3].sort(a,b -> a < b) +[1, 2, 3].reverse().first().abs() + 5 ^ 2 + +let f5 = x -> x ^ 2 +let g2 = x, y, z -> x * y * z +let h2 = -> "salut" +getCell(-> 12) + +let o2 = i,j -> a,b -> i * b + j * a +a -> b -> 2 + a -> c, d -> 5 and print("yeah") +var res = getLife(x -> getID(x)) + +__debug(f1(1, 2, true)) +__debug(f2("salut")) +__debug(f4()()()) +__debug(f9(4, "salut")) +let ress = f10(5,6)(100) +__debug(ress) +__debug(ress[2]()) + + +// Accents + +let état = "test" +let àôé = 2 + + +// Opérations diverses + +2 + 1 +'bonjour ça va ?' 5 [1,2] + 4 a = (2 || b) c - 12 +let ccc = 16 + b - 5 +let ddd = 4 * "salut" / true +let x = 2['salut' - 1] && true || false xor 1 + +let vv = -15 +__debug(vv.abs()) + + +// Signes moins +-1 +2 - 5 +4 + -5 +-10 - -10 +- (5 - (-7)) - +-4 + +(-5 - -13) - (-3) + + +// Opérations sur plusieurs lignes +5 +-4 + +5 - +4 + +5 +- 4 + +b += +a +- +5 ++ +"a" + + +// Opérateurs préfixes + +-2 +not false +!"salut" +new a + +// Parenthèses + +(5) * (4 + 2) - 7 / (4 - (((5)))) +print(f(a) + 5) + +// Classes + +class Combo { + + let score = 12 + + let new = function(score = 0) { + this.score = score + } + + let b = x -> x + 1 + + let update = function() { + score = score + 1 + return score + } + let updateSimple = -> ++score +} + +class Test {} + +let combo = new Combo(12) +let combo2 = new Combo() +let combo3 = Combo.new(13) +combo3.score = 55 +combo3.update() + +// Closures + +var ext = 55 +var fun = function(a) { // [ext] + + let b = 12 + let c = function(d) { // [b, ext] + return function(e) { [a, b, ext] + return a + b + d + e + ext + } + } + return c +} + +// Opérateurs en mode fonctions + +var plus = + +let minus = - +let modulo = (%) +var ternary = (?:) +array.map(+) + +var array = [ [true, 10, 12], [false, 5, 7] ] +var res1 = array.map(?:).transform(10, ^, +) + +// Valeurs absolues + +var res2 = |pos - 12| +var dd = |5 - |pos + 5| | +var dist = |getX(celll) - getY(|cell.pos.x|)| + +12.abs() +12.5.abs() + +a === b +a !== b +!!a +!!!!!a + +a-- +b++ +--c +++d +a *= b +b -= a +c /= e +g %= r +p ^= z + +p = ++(i++ ^ --z % ++z--)-- +a -- +++ a +a = -b++ +12 + +// Opérateur de fonction + +var yoyo = x -> x + " !" +var yo = "salut" +yo ->= yoyo +yo ~= yoyo // yo = "salut !" +yo ()= yoyo + + +// Boucles for + +for ;; {} +for ;; do end +for (;;) {} +for ; ; { + print("salut") +} + +for let i;; {} +for let i = 0;; {} +for let i = 0; i < 10; i++ {} +for let i = 0, j; i < 10; i++ {} +for i,j,let k = 2, l = "salut"; k !== "lama"; l++ {} +for let i,j, let k ;; {} + +for let i; i < 10; i++ do + print(i + 100) +end + +// Foreach + +for i in array {} +for i,j in array {} +for i:j in array do end +for let i,j in array {} +for let i, let j in array {} + +for leek in getLeeks() do + print(leek.getLife()) + for i in leek.cells { + print(i + 1) + } + leek.cells.each(x -> print(x)) +end diff --git a/src/test/resources/ai/code/divisors.leek b/src/test/resources/ai/code/divisors.leek new file mode 100644 index 00000000..e44a6af5 --- /dev/null +++ b/src/test/resources/ai/code/divisors.leek @@ -0,0 +1,2 @@ +var divisors = function(n) { return [1..n.sqrt()].filter(x -> !(n % x) } +return divisors(1989); \ No newline at end of file diff --git a/src/test/resources/ai/code/dynamic_operators.leek b/src/test/resources/ai/code/dynamic_operators.leek new file mode 100644 index 00000000..c8a3eb6a --- /dev/null +++ b/src/test/resources/ai/code/dynamic_operators.leek @@ -0,0 +1,5 @@ +// Add more iterations to test the speed of a simple operator +var a = 12$ +for var i = 0; i < 100; ++i { + a * 5 +} diff --git a/src/test/resources/ai/code/euler1.leek b/src/test/resources/ai/code/euler1.leek new file mode 100644 index 00000000..3dc514f6 --- /dev/null +++ b/src/test/resources/ai/code/euler1.leek @@ -0,0 +1,10 @@ +var n = 100000 + +var x3 = floor(n / 3) +var d3 = (x3 * (x3 + 1) / 2 | 0) * 3 +var x5 = floor((n - 1) / 5) +var d5 = (x5 * (x5 + 1) / 2 | 0) * 5 +var x15 = floor((n - 1) / 15) +var d15 = (x15 * (x15 + 1) / 2 | 0) * 15 + +return d3 + d5 - d15; diff --git a/src/test/resources/ai/code/fact1000.leek b/src/test/resources/ai/code/fact1000.leek new file mode 100644 index 00000000..b7facc22 --- /dev/null +++ b/src/test/resources/ai/code/fact1000.leek @@ -0,0 +1,5 @@ +var n = 1m +for var i = 1m; i < 1000m; i = i + 1m { + n = n * i +} +return n diff --git a/src/test/resources/ai/code/fibonacci.leek b/src/test/resources/ai/code/fibonacci.leek new file mode 100644 index 00000000..9f460cb9 --- /dev/null +++ b/src/test/resources/ai/code/fibonacci.leek @@ -0,0 +1,2 @@ +var fib = function(n) { return n < 2 ? n : fib(n - 1) + fib(n - 2) } +return fib(30); \ No newline at end of file diff --git a/src/test/resources/ai/code/fibonacci_long.leek b/src/test/resources/ai/code/fibonacci_long.leek new file mode 100644 index 00000000..723cc0f3 --- /dev/null +++ b/src/test/resources/ai/code/fibonacci_long.leek @@ -0,0 +1,2 @@ +let fib = n => n < 2 ? n : fib(n - 1) + fib(n - 2) +fib(31l) \ No newline at end of file diff --git a/src/test/resources/ai/code/fibonacci_v12.leek b/src/test/resources/ai/code/fibonacci_v12.leek new file mode 100644 index 00000000..98f27c7b --- /dev/null +++ b/src/test/resources/ai/code/fibonacci_v12.leek @@ -0,0 +1,2 @@ +let fib = n => n < 2 ? n : fib(n - 1) + fib(n - 2) +fib(30) \ No newline at end of file diff --git a/src/test/resources/ai/code/fold_left.leek b/src/test/resources/ai/code/fold_left.leek new file mode 100644 index 00000000..60c5416b --- /dev/null +++ b/src/test/resources/ai/code/fold_left.leek @@ -0,0 +1 @@ +return arrayFoldLeft([1, 3, 4, 2, 7, 5, 8, 9, 6], function(acc, x) { push(acc, {w: x}) return acc }, []) diff --git a/src/test/resources/ai/code/fold_left_2.leek b/src/test/resources/ai/code/fold_left_2.leek new file mode 100644 index 00000000..b43a37b6 --- /dev/null +++ b/src/test/resources/ai/code/fold_left_2.leek @@ -0,0 +1,2 @@ +function ppair(p, v) { {p: p, v: v} } +[1, 3, 4, 2, 7, 5, 8, 9, 6].foldLeft((pq, x) -> ppair(x, pq), {}) diff --git a/src/test/resources/ai/code/fold_right.leek b/src/test/resources/ai/code/fold_right.leek new file mode 100644 index 00000000..c691fb5e --- /dev/null +++ b/src/test/resources/ai/code/fold_right.leek @@ -0,0 +1 @@ +[1, 3, 4, 2, 7, 5, 8, 9, 6].foldRight((x, acc) -> acc.push({w: x}), []) diff --git a/src/test/resources/ai/code/fold_right_2.leek b/src/test/resources/ai/code/fold_right_2.leek new file mode 100644 index 00000000..dfad76a3 --- /dev/null +++ b/src/test/resources/ai/code/fold_right_2.leek @@ -0,0 +1,2 @@ +function ppair(p, v) { {p: p, v: v} } +[1, 3, 4, 2, 7, 5, 8, 9, 6].foldRight((pq, x) -> ppair(x, pq), {}) diff --git a/src/test/resources/ai/code/french.leek b/src/test/resources/ai/code/french.leek new file mode 100644 index 00000000..2a35e717 --- /dev/null +++ b/src/test/resources/ai/code/french.leek @@ -0,0 +1,63 @@ +var french = function(n) { + var T = '-' + var V = '' + var x = split('s un deux trois quatre cinq six sept huit neuf dix onze douze treize quatorze quinze seize dix vingt trente quarante cinquante soixante quatre-vingt cent', ' ') + var b = n % 100 + var d = 17 + b / 10 + var u = n % 10 + var c = floor(n / 100) + var e = (u - 1) ? T : '-et-' + var L = floor(log10(n) / 3) + var H = pow(1000, L) + var v = floor(n / H) + if (x[d] == '') { d-- u += 10 } + var k = L > 1 ? 1 : 0 + var i = [T, ' '][k] + + if (L < 1) { + if (c) { + var r = '' + if (c > 1) { r = french(c) r += T } + r += x[27] + if (!(c * !b < 2)) { r += x[0] } + r += (b ? T : V) + french(b) + return r + } else { + if (n < 17) { + return x[n + 1] + } else { + if (n < 20) { + return x[11] + T + x[u + 1] + } else { + var r = x[d] + if (u) { + return r + e + french(u) + } else { + if (d == 25) { r += x[0] } + return r + } + } + } + } + } else { + var r = '' + if (L * v > 1) { r = french(v) + i } + r += 'mill' + r += ['', 'e', 'ion', 'iard'][L] + if (!(v < 2 or L < 2)) { r += x[0] } + if (n) { r += i } + n %= H + r += french(n) + return r + } +} + +return french(168489999); + +/* +for (var j = 0; j < 3; ++j) { + for (var i = 1; i < 5; ++i) { + var n = Number.randInt(1000 ** j, 1000 ** (j + 1)) + System.print(n + " ==> " + french(n)) + } +} diff --git a/src/test/resources/ai/code/french.min.leek b/src/test/resources/ai/code/french.min.leek new file mode 100644 index 00000000..64105b09 --- /dev/null +++ b/src/test/resources/ai/code/french.min.leek @@ -0,0 +1,5 @@ +function _(n) { + var T = '-', V = '', x = split('s un deux trois quatre cinq six sept huit neuf dix onze douze treize quatorze quinze seize dix vingt trente quarante cinquante soixante quatre-vingt cent' ' '), b = n % 100, d = (17 + b / 10) | 0, u = n % 10, c = floor(n / 100), e = u - 1 ? T : '-et-', L = (log10(n) / 3) | 0, H = 1000 ** L, v = floor(n / H), i = [T ' ' x[d] ? 0 : d-- or (u += 10)][L > 1] + return (L < 1) ? c ? (c > 1 ? _(c) + T : '') + x[27] + x[c * !b < 2] + (b ? T : '') + _(b) : n < 17 ? x[n + 1] : n < 20 ? x[11] + '-' + x[u + 1] : x[d] + (u ? e + _(u) : x[d != 25]) : (L * v > 1 ? _(v) + i : '') + 'mill' + [n %= H 'e' 'ion' 'iard'][L] + x[v < 2 or L < 2] + (n ? i : '') + _(n) +} +return _(987654321012); diff --git a/src/test/resources/ai/code/gcd.leek b/src/test/resources/ai/code/gcd.leek new file mode 100644 index 00000000..8028387f --- /dev/null +++ b/src/test/resources/ai/code/gcd.leek @@ -0,0 +1,10 @@ +var gcd = function(a, b) { + while (b > 0) { + var c = a % b + a = b + b = c + } + return a +} + +return gcd(163231, 135749); \ No newline at end of file diff --git a/src/test/resources/ai/code/global_functions_1.leek b/src/test/resources/ai/code/global_functions_1.leek new file mode 100644 index 00000000..d2e4c8ab --- /dev/null +++ b/src/test/resources/ai/code/global_functions_1.leek @@ -0,0 +1,7 @@ +var v = is_good(5, 6) + +function is_good(x, y) { + return x == y +} + +return v; diff --git a/src/test/resources/ai/code/global_functions_2.leek b/src/test/resources/ai/code/global_functions_2.leek new file mode 100644 index 00000000..56d770b1 --- /dev/null +++ b/src/test/resources/ai/code/global_functions_2.leek @@ -0,0 +1,9 @@ +function is_good(x, y) { + return inc(x) == inc(y) +} + +function inc(x) { + return x + 1 +} + +return [is_good(5, 6), is_good(6, 6)]; diff --git a/src/test/resources/ai/code/knapsack.leek b/src/test/resources/ai/code/knapsack.leek new file mode 100644 index 00000000..983b458f --- /dev/null +++ b/src/test/resources/ai/code/knapsack.leek @@ -0,0 +1,33 @@ +var items = [ + [37, 3], [47, 10], [28, 5], [89, 7], [55, 11], [11, 2], [75, 4] +] + +var all = []; +var aux; +aux = function(@current, i, tp, added, last) { + + if (count(current[1])) push(all, current); + var item_count = count(items); + + for (var j = i; j < item_count; ++j) { + var item = @items[j]; + var item_id = item[0]; + var cost = item[1]; + + if (cost > tp) continue; + + var new_added = added; + new_added[item_id] = true; + + var copy = current; + if (item_id === last) { + copy[1][count(copy[1]) - 1][2]++; + } else { + push(copy[1], @[item, cost, 1]); + } + copy[0] += cost; + aux(copy, j, tp - cost, new_added, item_id); + } +}; +aux([0, []], 0, 25, [], -1); +return count(all); \ No newline at end of file diff --git a/src/test/resources/ai/code/knapsack_2.leek b/src/test/resources/ai/code/knapsack_2.leek new file mode 100644 index 00000000..31caff9b --- /dev/null +++ b/src/test/resources/ai/code/knapsack_2.leek @@ -0,0 +1,32 @@ +var items = [ + [37, 3], [47, 10], [28, 5], [89, 7], [55, 11], [11, 2], [75, 4] +] + +var all = []; +var aux = function(current, i, tp, added, last) { + + if (count(current[1])) push(all, current); + var item_count = count(items); + + for (var j = i; j < item_count; ++j) { + var item = items[j]; + var item_id = item[0]; + var cost = item[1]; + + if (cost > tp) continue; + + var new_added = clone(added); + new_added[item_id] = true; + + var copy = current; + if (item_id === last) { + copy[1][count(copy[1]) - 1][2]++; + } else { + push(copy[1], [item, cost, 1]); + } + copy[0] += cost; + aux(copy, j, tp - cost, new_added, item_id); + } +}; +aux([0, []], 0, 25, [], -1); +return count(all); \ No newline at end of file diff --git a/src/test/resources/ai/code/match.leek b/src/test/resources/ai/code/match.leek new file mode 100644 index 00000000..e2212950 --- /dev/null +++ b/src/test/resources/ai/code/match.leek @@ -0,0 +1,6 @@ +let v = "oui" +match v { + "oui": print("Yeah!") + "non": print("snif") + ..: print("ok") +} \ No newline at end of file diff --git a/src/test/resources/ai/code/mul_array.leek b/src/test/resources/ai/code/mul_array.leek new file mode 100644 index 00000000..6a85f97b --- /dev/null +++ b/src/test/resources/ai/code/mul_array.leek @@ -0,0 +1,26 @@ +// TODO allow 'new' token in v1 + +function _mulArrays(arrays) { + + if (count(arrays) == 0) return [] + + var aux = function(stack, neww) { + var res = [] + for (var n in neww) { + for (var e in stack) { + push(res, e + [n]) + } + } + return res + } + + var res = arrayMap(shift(arrays), function(e) { + return [e] + }) + for (var array in arrays) { + res = aux(res, array) + } + return res +} + +_mulArrays([[1, 2, 3], [4, 5, 6]]) diff --git a/src/test/resources/ai/code/pow5.leek b/src/test/resources/ai/code/pow5.leek new file mode 100644 index 00000000..b99dcf99 --- /dev/null +++ b/src/test/resources/ai/code/pow5.leek @@ -0,0 +1,14 @@ +function pow5(p) { + var x = 1m + var n = 0 + while (p > 0) { + if ((p & 1) != 0) { + x *= 5m ** (1 << n) + } + p = p >> 1 + n++ + } + x +} + +pow5(57) \ No newline at end of file diff --git a/src/test/resources/ai/code/primes.leek b/src/test/resources/ai/code/primes.leek new file mode 100644 index 00000000..e578b64d --- /dev/null +++ b/src/test/resources/ai/code/primes.leek @@ -0,0 +1,17 @@ +var f = function(number) { + for (var k = 1; 36 * k * k - 12 * k < number; k += 1) { + if ((number % (6 * k + 1) == 0) or (number % (6 * k - 1) == 0)) { + return false + } + } + return true +} + +var c = 2 + +for (var i = 5; i < 1000000; i += 6) { + if (f(i)) ++c + if (f(i + 2)) ++c +} + +return c; diff --git a/src/test/resources/ai/code/primes_gmp.leek b/src/test/resources/ai/code/primes_gmp.leek new file mode 100644 index 00000000..84eb8306 --- /dev/null +++ b/src/test/resources/ai/code/primes_gmp.leek @@ -0,0 +1,9 @@ +var c = 0 + +for var i = 1m; i < 100000m; i += 2m { + if i.isPrime() > 0 { + c++ + } +} + +return c diff --git a/src/test/resources/ai/code/product_coproduct.leek b/src/test/resources/ai/code/product_coproduct.leek new file mode 100644 index 00000000..ba9226e4 --- /dev/null +++ b/src/test/resources/ai/code/product_coproduct.leek @@ -0,0 +1,8 @@ +function coProductN(n, i, acc) { + if (i == n) { + return i * acc; + } else { + return coProductN(n, i + 1, i * acc); + } +} +coProductN(69m, 1m, 1m) diff --git a/src/test/resources/ai/code/product_n.leek b/src/test/resources/ai/code/product_n.leek new file mode 100644 index 00000000..9c8cb6d6 --- /dev/null +++ b/src/test/resources/ai/code/product_n.leek @@ -0,0 +1,8 @@ +function productN(n) { + if (n == 1) { + 1m; + } else { + n * productN(n - 1); + } +} +productN(7m) diff --git a/src/test/resources/ai/code/product_n_arrays.leek b/src/test/resources/ai/code/product_n_arrays.leek new file mode 100644 index 00000000..99447dd8 --- /dev/null +++ b/src/test/resources/ai/code/product_n_arrays.leek @@ -0,0 +1,8 @@ +function productN(n) { + if n == [1] { + [1] + } else { + [n[0] * productN([n[0] - 1])[0]] + } +} +productN([7]) diff --git a/src/test/resources/ai/code/product_n_return.leek b/src/test/resources/ai/code/product_n_return.leek new file mode 100644 index 00000000..2a605cac --- /dev/null +++ b/src/test/resources/ai/code/product_n_return.leek @@ -0,0 +1,8 @@ +function productN(n) { + if (n == 1) { + return 1m; + } else { + return n * productN(n - 1); + } +} +productN(30m) diff --git a/src/test/resources/ai/code/project_euler_20.ls b/src/test/resources/ai/code/project_euler_20.ls new file mode 100644 index 00000000..b9f21434 --- /dev/null +++ b/src/test/resources/ai/code/project_euler_20.ls @@ -0,0 +1,30 @@ +var digits = [1] + +for var p = 1; p <= 100; p++ { + for var j = 0; j < digits.size(); j++ { + digits[j] *= p + } + for var i = 0; i < digits.size(); i++ { + if (9 < digits[i]) { + let m = digits[i] % 10 + var q = (digits[i] - m) / 10 + digits[i] = m + if (i < digits.size() - 1) { + digits[i + 1] += q + } else { + digits += q + break + } + } + } + var n = digits.size() - 1 + while (9 < digits[n]) { + let mo = digits[n] % 10 + let qu = (digits[n] - mo) / 10 + digits[n] = mo + digits += qu + n++ + } +} +// print(~digits) +digits.sum() diff --git a/src/test/resources/ai/code/project_euler_61.ls b/src/test/resources/ai/code/project_euler_61.ls new file mode 100644 index 00000000..b50f5106 --- /dev/null +++ b/src/test/resources/ai/code/project_euler_61.ls @@ -0,0 +1,110 @@ +var tri = [] +for (var t = 0; t < 142; ++t) { + print(t * (t + 1) / 2) + //Array.insert(tri, true, t * (t + 1) / 2) +} + +print(tri) + +/* +var squ = [] +for (var s = 0; s < 102; ++s) { Array.insert(squ, true, s * s) } +var pen = [] +for (var p = 0; p < 83; ++p) { Array.insert(pen, true, p * (3 * p - 1) / 2) } +var hex = [] +for (var h = 0; h < 72; ++h) { Array.insert(hex, true, h * (2 * h - 1)) } +var hep = [] +for (var g = 0; g < 65; ++g) { Array.insert(hep, true, g * (5 * g - 3) / 2) } +var oct = [] +for (var o = 0; o < 61; ++o) { Array.insert(oct, true, o * (3 * o - 2)) } + +var P = [tri, squ, pen, hex, hep, oct] +var poly = [0, 0, 0, 0, 0, 0] + +print(P) + +/* + +var NUL = [] +for (var nul = 1000; nul <= 9999; ++nul) { + if (tri[nul] == null && squ[nul] == null && pen[nul] == null && + hex[nul] == null && hep[nul] == null && oct[nul] == null) { + Array.insert(NUL, true, nul) + } +} +var min = 1000 +var max = 9999 + +for (var a = min; a <= max; ++a) { + if NUL[a] continue + + for (var pa = 0; pa < 6; ++pa) { + + if (P[pa][a] == null) continue + poly[pa] = 1 + var al = a % 100 + var af = (a - al) / 100 + + for (var b = Number.max(min, al * 100); b < Number.min(max, (al + 1) * 100); ++b) { + if NUL[b] continue + + for (var pb = 0; pb < 6; ++pb) { + + if (poly[pb] or P[pb][b] == null) continue + poly[pb] = 1 + var bl = b % 100 + + for (var c = Number.max(min, bl * 100); c < Number.min(max, (bl + 1) * 100); ++c) { + if NUL[c] continue + + for (var pc = 0; pc < 6; ++pc) { + + if (poly[pc] or P[pc][c] == null) continue + poly[pc] = 1 + var cl = c % 100 + + for (var d = Number.max(min, cl * 100); d < Number.min(max, (cl + 1) * 100); ++d) { + if NUL[d] continue + + for (var pd = 0; pd < 6; ++pd) { + + if (poly[pd] or P[pd][d] == null) continue + poly[pd] = 1 + var dl = d % 100 + + for (var e = Number.max(min, dl * 100); e < Number.min(max, (dl + 1) * 100); ++e) { + if NUL[e] continue + + for (var pe = 0; pe < 6; ++pe) { + + if (poly[pe] or P[pe][e] == null) continue + poly[pe] = 1 + var el = e % 100 + + for (var f = Number.max(min, el * 100); f < Number.min(max, (el + 1) * 100); ++f) { + if NUL[f] continue + + if (f % 100 == af) { + for (var pf = 0; pf < 6; ++pf) { + if (!poly[pf] and P[pf][f]) { + print(a + " " + b + " " + c + " " + d + " " + e + " " + f) + return "sum : " + (a + b + c + d + e + f) + } + } + } + } + poly[pe] = 0 + } + } + poly[pd] = 0 + } + } + poly[pc] = 0 + } + } + poly[pb] = 0 + } + } + poly[pa] = 0 + } +} \ No newline at end of file diff --git a/src/test/resources/ai/code/quine.leek b/src/test/resources/ai/code/quine.leek new file mode 100644 index 00000000..af6745c8 --- /dev/null +++ b/src/test/resources/ai/code/quine.leek @@ -0,0 +1 @@ +var q=Json.encode('')[0]var a="var q=Json.encode('')[0]var a="+q var b="System.print(a+a+'+q var b='+q+b+q+b)"System.print(a+a+'+q var b='+q+b+q+b) diff --git a/src/test/resources/ai/code/quine_zwik.leek b/src/test/resources/ai/code/quine_zwik.leek new file mode 100644 index 00000000..ce5f3ed9 --- /dev/null +++ b/src/test/resources/ai/code/quine_zwik.leek @@ -0,0 +1,16 @@ +var s=charAt('\"'0); +var q=charAt('\"'1); +var l=[]; +pushAll(l,[ +"var s=charAt('\"'0);", +"var q=charAt('\"'1);", +"var l=[];", +"pushAll(l,[", +"]);", +"for(var i=0;i<4;i++)debug(replace(l[i],q,s+s+q));", +"for(var i=0;i 10 ? fun_real(x - 0.5) : x } + +return fun_int(20); diff --git a/src/test/resources/ai/code/recursive_2_vars.leek b/src/test/resources/ai/code/recursive_2_vars.leek new file mode 100644 index 00000000..d4590eb1 --- /dev/null +++ b/src/test/resources/ai/code/recursive_2_vars.leek @@ -0,0 +1,12 @@ +var ack = function(m, n) { + if (m == 0) { + return n + 1 + } else { + if (n == 0) { + return ack(m - 1, 1) + } else { + return ack(m - 1, ack(m, n - 1)) + } + } +} +return ack(3, 7); diff --git a/src/test/resources/ai/code/recursive_2_versions.leek b/src/test/resources/ai/code/recursive_2_versions.leek new file mode 100644 index 00000000..953bb145 --- /dev/null +++ b/src/test/resources/ai/code/recursive_2_versions.leek @@ -0,0 +1,12 @@ +let fun = n -> { + if n < 10 { + n + } else { + if (n.isInteger()) { + fun(n - 0.5) + } else { + fun((n - 0.5).floor()) + } + } +} +fun(20) diff --git a/src/test/resources/ai/code/recursive_conditions.leek b/src/test/resources/ai/code/recursive_conditions.leek new file mode 100644 index 00000000..6f70b603 --- /dev/null +++ b/src/test/resources/ai/code/recursive_conditions.leek @@ -0,0 +1,22 @@ +function fact(x) { + let a = if x < 5 { + if x == 1 { + 1 + } else { + fact(x + 1) * x + } + } else { + if x > 10 { + fact(x - 1) * x + } else { + 1 + } + } + if a % 2 { + a + } else { + fact(4) + a + } +} + +fact(12) diff --git a/src/test/resources/ai/code/recursive_conditions_2.leek b/src/test/resources/ai/code/recursive_conditions_2.leek new file mode 100644 index 00000000..1797de30 --- /dev/null +++ b/src/test/resources/ai/code/recursive_conditions_2.leek @@ -0,0 +1,22 @@ +function hello(x) { + let a = if x < 5 { + if x == 1 { + '_' + } else { + hello(x + 1) + x + } + } else { + if x > 10 { + hello(x - 1) + x + } else { + '-' + } + } + if x % 2 { + a + } else { + hello(4) + a + } +} + +hello(12) diff --git a/src/test/resources/ai/code/return_in_function.leek b/src/test/resources/ai/code/return_in_function.leek new file mode 100644 index 00000000..c26f5147 --- /dev/null +++ b/src/test/resources/ai/code/return_in_function.leek @@ -0,0 +1,6 @@ +function f() { + var cells = [12] + push(cells, 1) + return cells +} +return count(f()); \ No newline at end of file diff --git a/src/test/resources/ai/code/strings.leek b/src/test/resources/ai/code/strings.leek new file mode 100644 index 00000000..133667a6 --- /dev/null +++ b/src/test/resources/ai/code/strings.leek @@ -0,0 +1,15 @@ +var m = ['A', 'T', 'C', 'G'] +var count = 0 +var tests = 500 + +for (var k = 0; k < tests; k++) { + + var adn = '' + for (var j = 0; j < 200; j++) { + adn += m[randInt(0, 4)] + } + var c = contains(adn, 'GAGA'); + if (c) count++ +} + +return abs(100 * (count / tests) - 52) < 12; // ~ 52% diff --git a/src/test/resources/ai/code/swap.leek b/src/test/resources/ai/code/swap.leek new file mode 100644 index 00000000..f13bad0e --- /dev/null +++ b/src/test/resources/ai/code/swap.leek @@ -0,0 +1,23 @@ +let siftUp = (c, pq) -> { + if (c > 0) { + let p = (c - 1) >> 1 + if (pq[c].p < pq[p].p) { + pq[p] <=> pq[c] + return siftUp(p, pq) + } else { + return pq + } + } else { + return pq + } +} + +let pqInsert = (p, pq) -> { + siftUp(pq.size(), pq.push({p: p})) +} + +var pq = [{p: 4}] +pqInsert(5, pq) +pqInsert(3, pq) +pqInsert(12, pq) +pqInsert(1, pq) diff --git a/src/test/resources/ai/code/tarai.leek b/src/test/resources/ai/code/tarai.leek new file mode 100644 index 00000000..b3408230 --- /dev/null +++ b/src/test/resources/ai/code/tarai.leek @@ -0,0 +1,9 @@ +function tarai(x, y, z) { + if (x <= y) { + return y + } else { + return tarai(tarai(x - 1, y, z), tarai(y - 1, z, x), tarai(z - 1, x, y)) + } +} + +return tarai(16, 13, 5); \ No newline at end of file diff --git a/src/test/resources/ai/code/text_analysis.leek b/src/test/resources/ai/code/text_analysis.leek new file mode 100644 index 00000000..17c577dc --- /dev/null +++ b/src/test/resources/ai/code/text_analysis.leek @@ -0,0 +1,9 @@ +var text = "Le Poireau (Allium porrum) est une espèce de plante herbacée vivace largement cultivée comme plante potagère pour ses feuilles (pseudo-tiges) consommées comme légumes. +Il appartient à la famille des Amaryllidacées (précédemment famille des Liliacées puis des Alliacées). +Noms communs : poireau, porreau, poirée, poirette, asperge du pauvre." + +let lines = text.lines().size() +let words = text.wordCount() +let chars = text.lines().map(x -> x.size()).foldLeft((x, y -> x + y), 0) + +return [lines, words, chars]; diff --git a/src/test/resources/ai/code/trivial.leek b/src/test/resources/ai/code/trivial.leek new file mode 100644 index 00000000..ce4cc792 --- /dev/null +++ b/src/test/resources/ai/code/trivial.leek @@ -0,0 +1 @@ +return 2 \ No newline at end of file diff --git a/src/test/resources/ai/code/two_functions.leek b/src/test/resources/ai/code/two_functions.leek new file mode 100644 index 00000000..139d27b4 --- /dev/null +++ b/src/test/resources/ai/code/two_functions.leek @@ -0,0 +1,3 @@ +var ppair = function(p, v) { return {p: p, v: v} } +var pqSingleton = function(p, v) { return [{p: p, v: v}] } +return [ppair(2, 5), pqSingleton(3, 6)] diff --git a/src/test/resources/ai/euler/pe001.leek b/src/test/resources/ai/euler/pe001.leek new file mode 100644 index 00000000..06697166 --- /dev/null +++ b/src/test/resources/ai/euler/pe001.leek @@ -0,0 +1,7 @@ +var sum = 0 +for (var n = 0; n < 1000; ++n) { + if (n % 3 == 0 or n % 5 == 0) { + sum += n + } +} +return sum; diff --git a/src/test/resources/ai/euler/pe002.leek b/src/test/resources/ai/euler/pe002.leek new file mode 100644 index 00000000..c6496c1b --- /dev/null +++ b/src/test/resources/ai/euler/pe002.leek @@ -0,0 +1,12 @@ +var f1 = 1 +var f2 = 1 +var sum = 0 +while (f2 < 4000000) { + var n = f1 + f2 + f1 = f2 + f2 = n + if (n % 2 == 0) { + sum += n + } +} +return sum; diff --git a/src/test/resources/ai/euler/pe003.leek b/src/test/resources/ai/euler/pe003.leek new file mode 100644 index 00000000..d87ac224 --- /dev/null +++ b/src/test/resources/ai/euler/pe003.leek @@ -0,0 +1,7 @@ +var p = 600851475143 +for (var i = 2; i * i <= p; i++) { + if (p % i == 0) { + p /= i + } +} +return p | 0; diff --git a/src/test/resources/ai/euler/pe004.leek b/src/test/resources/ai/euler/pe004.leek new file mode 100644 index 00000000..2d43197f --- /dev/null +++ b/src/test/resources/ai/euler/pe004.leek @@ -0,0 +1,10 @@ +var max = 0 +for (var a = 999; a >= 100; --a) { + for (var b = a; b >= 100; --b) { + let p = a × b + if (p > max and p.isPalindrome()) { + max = p + } + } +} +return max diff --git a/src/test/resources/ai/euler/pe005.leek b/src/test/resources/ai/euler/pe005.leek new file mode 100644 index 00000000..db4fafd6 --- /dev/null +++ b/src/test/resources/ai/euler/pe005.leek @@ -0,0 +1,15 @@ +/* +1 (2) (3) 4 (5) 6 (7) 8 9 10 (11) 12 (13) 14 15 16 (17) 18 (19) 20 + 4 = 2 ^ 2 : two 2 needed +(6 = 2 * 3) + 8 = 2 ^ 3 : three 2 needed + 9 = 3 ^ 2 : two 3 needed +(10 = 5 * 2) +(12 = 3 * 2 ^ 2) +(14 = 2 * 7) + 16 = 2 ^ 4 : four 2 needed +(18 = 2 * 3 ^ 2) +(20 = 2 ^ 2 * 5) +*/ + +return (2 ** 4) * (3 ** 2) * 5 * 7 * 11 * 13 * 17 * 19; diff --git a/src/test/resources/ai/euler/pe006.leek b/src/test/resources/ai/euler/pe006.leek new file mode 100644 index 00000000..dd7b7431 --- /dev/null +++ b/src/test/resources/ai/euler/pe006.leek @@ -0,0 +1,9 @@ +var sum = 0 +var squares_sum = 0 + +for (var n = 1; n <= 100; ++n) { + sum += n + squares_sum += n * n +} + +return sum * sum - squares_sum; diff --git a/src/test/resources/ai/euler/pe007.leek b/src/test/resources/ai/euler/pe007.leek new file mode 100644 index 00000000..46fc465d --- /dev/null +++ b/src/test/resources/ai/euler/pe007.leek @@ -0,0 +1,19 @@ +function is_prime(n) { + var s = sqrt(n) + for (var d = 3; d <= s; d += 2) { + if (n % d == 0) { + return false + } + } + return true +} + +var count = 2 +var i = 3 +while (i) { + if (is_prime(i += 2)) { + if (++count == 10001) { + if (true) return i + } + } +} diff --git a/src/test/resources/ai/euler/pe008.leek b/src/test/resources/ai/euler/pe008.leek new file mode 100644 index 00000000..b31b641f --- /dev/null +++ b/src/test/resources/ai/euler/pe008.leek @@ -0,0 +1,36 @@ +let S = +"73167176531330624919225119674426574742355349194934" + +"96983520312774506326239578318016984801869478851843" + +"85861560789112949495459501737958331952853208805511" + +"12540698747158523863050715693290963295227443043557" + +"66896648950445244523161731856403098711121722383113" + +"62229893423380308135336276614282806444486645238749" + +"30358907296290491560440772390713810515859307960866" + +"70172427121883998797908792274921901699720888093776" + +"65727333001053367881220235421809751254540594752243" + +"52584907711670556013604839586446706324415722155397" + +"53697817977846174064955149290862569321978468622482" + +"83972241375657056057490261407972968652414535100474" + +"82166370484403199890008895243450658541227588666881" + +"16427171479924442928230863465674813919123162824586" + +"17866458359124566529476545682848912883142607690042" + +"24219022671055626321111109370544217506941658960408" + +"07198403850962455444362981230987879927244284909188" + +"84580156166097919133875499200524063689912560717606" + +"05886116467109405077541002256983155200055935729725" + +"71636269561882670428252483600823257530420752963450" + +var N = [] +for var s in S.split('') { + N += String.code(s) - String.code('0') +} + +var max = 1l +for var i = 0; i < N.size() - 12; ++i { + var prod = N[i : i + 12].foldLeft((x, y -> x × y), 1.0) + var prod_int = String.number('' + prod) + if max < prod_int { + max = prod_int + } +} +return max diff --git a/src/test/resources/ai/euler/pe009.leek b/src/test/resources/ai/euler/pe009.leek new file mode 100644 index 00000000..82c1bb68 --- /dev/null +++ b/src/test/resources/ai/euler/pe009.leek @@ -0,0 +1,8 @@ +for (var a = 1; a < 1000; a++) { + for (var b = a + 1; b < 1000 - a; b++) { + var c = 1000 - a - b + if (a * a + b * b == c * c) { + return a * b * c + } + } +} diff --git a/src/test/resources/ai/euler/pe010.leek b/src/test/resources/ai/euler/pe010.leek new file mode 100644 index 00000000..c37010d9 --- /dev/null +++ b/src/test/resources/ai/euler/pe010.leek @@ -0,0 +1,20 @@ +var N = 2000000 +var numbers = [] +numbers.fill(1, N + 1) +numbers[1] = 0 + +var S = N.sqrt().floor() +for var i = 2; i < S; ++i { + if numbers[i] { + for (var j = i * i; j <= N; j += i) { + numbers[j] = 0 + } + } +} +numbers.size() + +var sum = 0l +for i, p in numbers { + if p sum += i +} +sum diff --git a/src/test/resources/ai/euler/pe011.leek b/src/test/resources/ai/euler/pe011.leek new file mode 100644 index 00000000..db4c173b --- /dev/null +++ b/src/test/resources/ai/euler/pe011.leek @@ -0,0 +1,60 @@ +let grid = [08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08 +49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00 +81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65 +52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91 +22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80 +24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50 +32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70 +67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21 +24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72 +21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95 +78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92 +16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57 +86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58 +19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40 +04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66 +88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69 +04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36 +20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16 +20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54 +01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48].chunk(20) + +var max = 0 + +// TODO in future use advanced array range operators to select +// diagonally or backwards + +for var j = 0; j < grid.size(); ++j { // TODO use |grid| instead of grid.size() + for var i = 0; i < grid.size() - 3; ++i { + + // horizontal + max = max.max(grid[j][i:i + 3].product()) + + // vertical + var hprod = 1 + for (var k = 0; k < 4; ++k) { + hprod *= grid[i + k][j] + } + max = max.max(hprod) + + if (j < grid.size() - 3) { + // diagonal 1 + var dprod1 = 1 + for (var k = 0; k < 4; ++k) { + dprod1 *= grid[i + k][j + k] + } + max = max.max(dprod1) + + if (i > 3) { + // diagonal 2 + var dprod2 = 1 + for (var k = 0; k < 4; ++k) { + dprod2 *= grid[i - k][j + k] + } + max = max.max(dprod2) + } + } + } +} + +max diff --git a/src/test/resources/ai/euler/pe012.leek b/src/test/resources/ai/euler/pe012.leek new file mode 100644 index 00000000..88bac4cb --- /dev/null +++ b/src/test/resources/ai/euler/pe012.leek @@ -0,0 +1,29 @@ +var n = 50000 +var N = 500 +var d = [] +fill(d, 0, n + 2) + +for (var i = 1; i <= n + 1; i++) { + for (var j = i; j <= n + 1; j += i) { + d[j]++ + } +} + +var r = 0 +var t = 0 +for (var i = 1; i <= n; i++) { + var res = 0 + if (i & 1) { + res = d[i] * d[(i + 1) / 2] + } else { + res = d[i / 2] * d[i + 1] + } + if (res > t) { + t = res + } + if (res > N) { + r = i * (i + 1) / 2 | 0 + break + } +} +return r; diff --git a/src/test/resources/ai/euler/pe013.leek b/src/test/resources/ai/euler/pe013.leek new file mode 100644 index 00000000..5e87a457 --- /dev/null +++ b/src/test/resources/ai/euler/pe013.leek @@ -0,0 +1,102 @@ +var sum = 37107287533902102798797998220837590246510135740250 + +46376937677490009712648124896970078050417018260538 + +74324986199524741059474233309513058123726617309629 + +91942213363574161572522430563301811072406154908250 + +23067588207539346171171980310421047513778063246676 + +89261670696623633820136378418383684178734361726757 + +28112879812849979408065481931592621691275889832738 + +44274228917432520321923589422876796487670272189318 + +47451445736001306439091167216856844588711603153276 + +70386486105843025439939619828917593665686757934951 + +62176457141856560629502157223196586755079324193331 + +64906352462741904929101432445813822663347944758178 + +92575867718337217661963751590579239728245598838407 + +58203565325359399008402633568948830189458628227828 + +80181199384826282014278194139940567587151170094390 + +35398664372827112653829987240784473053190104293586 + +86515506006295864861532075273371959191420517255829 + +71693888707715466499115593487603532921714970056938 + +54370070576826684624621495650076471787294438377604 + +53282654108756828443191190634694037855217779295145 + +36123272525000296071075082563815656710885258350721 + +45876576172410976447339110607218265236877223636045 + +17423706905851860660448207621209813287860733969412 + +81142660418086830619328460811191061556940512689692 + +51934325451728388641918047049293215058642563049483 + +62467221648435076201727918039944693004732956340691 + +15732444386908125794514089057706229429197107928209 + +55037687525678773091862540744969844508330393682126 + +18336384825330154686196124348767681297534375946515 + +80386287592878490201521685554828717201219257766954 + +78182833757993103614740356856449095527097864797581 + +16726320100436897842553539920931837441497806860984 + +48403098129077791799088218795327364475675590848030 + +87086987551392711854517078544161852424320693150332 + +59959406895756536782107074926966537676326235447210 + +69793950679652694742597709739166693763042633987085 + +41052684708299085211399427365734116182760315001271 + +65378607361501080857009149939512557028198746004375 + +35829035317434717326932123578154982629742552737307 + +94953759765105305946966067683156574377167401875275 + +88902802571733229619176668713819931811048770190271 + +25267680276078003013678680992525463401061632866526 + +36270218540497705585629946580636237993140746255962 + +24074486908231174977792365466257246923322810917141 + +91430288197103288597806669760892938638285025333403 + +34413065578016127815921815005561868836468420090470 + +23053081172816430487623791969842487255036638784583 + +11487696932154902810424020138335124462181441773470 + +63783299490636259666498587618221225225512486764533 + +67720186971698544312419572409913959008952310058822 + +95548255300263520781532296796249481641953868218774 + +76085327132285723110424803456124867697064507995236 + +37774242535411291684276865538926205024910326572967 + +23701913275725675285653248258265463092207058596522 + +29798860272258331913126375147341994889534765745501 + +18495701454879288984856827726077713721403798879715 + +38298203783031473527721580348144513491373226651381 + +34829543829199918180278916522431027392251122869539 + +40957953066405232632538044100059654939159879593635 + +29746152185502371307642255121183693803580388584903 + +41698116222072977186158236678424689157993532961922 + +62467957194401269043877107275048102390895523597457 + +23189706772547915061505504953922979530901129967519 + +86188088225875314529584099251203829009407770775672 + +11306739708304724483816533873502340845647058077308 + +82959174767140363198008187129011875491310547126581 + +97623331044818386269515456334926366572897563400500 + +42846280183517070527831839425882145521227251250327 + +55121603546981200581762165212827652751691296897789 + +32238195734329339946437501907836945765883352399886 + +75506164965184775180738168837861091527357929701337 + +62177842752192623401942399639168044983993173312731 + +32924185707147349566916674687634660915035914677504 + +99518671430235219628894890102423325116913619626622 + +73267460800591547471830798392868535206946944540724 + +76841822524674417161514036427982273348055556214818 + +97142617910342598647204516893989422179826088076852 + +87783646182799346313767754307809363333018982642090 + +10848802521674670883215120185883543223812876952786 + +71329612474782464538636993009049310363619763878039 + +62184073572399794223406235393808339651327408011116 + +66627891981488087797941876876144230030984490851411 + +60661826293682836764744779239180335110989069790714 + +85786944089552990653640447425576083659976645795096 + +66024396409905389607120198219976047599490197230297 + +64913982680032973156037120041377903785566085089252 + +16730939319872750275468906903707539413042652315011 + +94809377245048795150954100921645863754710598436791 + +78639167021187492431995700641917969777599028300699 + +15368713711936614952811305876380278410754449733078 + +40789923115535562561142322423255033685442488917353 + +44889911501440648020369068063960672322193204149535 + +41503128880339536053299340368006977710650566631954 + +81234880673210146739058568557934581403627822703280 + +82616570773948327592232845941706525094512325230608 + +22918802058777319719839450180888072429661980811197 + +77158542502016545090413245809786882778948721859617 + +72107838435069186155435662884062257473692284509516 + +20849603980134001723930671666823555245252804609722 + +53503534226472524250874054075591789781264330331690 + +sum.string().substring(0, 10).number() diff --git a/src/test/resources/ai/euler/pe014.leek b/src/test/resources/ai/euler/pe014.leek new file mode 100644 index 00000000..5a7fa604 --- /dev/null +++ b/src/test/resources/ai/euler/pe014.leek @@ -0,0 +1,33 @@ +var N = 1000000 +var cache = [] +fill(cache, 0, N) +cache[1] = 1 + +var sequence_length = function(n) { + if (n >= N) { + return n % 2 ? + 1 + sequence_length(n * 3 + 1) + : + 1 + sequence_length(n / 2) + } + var c = cache[n] + if (c) return c + + return cache[n] = n % 2 ? + 1 + sequence_length(n * 3 + 1) + : + 1 + sequence_length(n / 2) +} + +var max = 0 +var n = 0 + +for (var i = 1; i < 1000000; i++) { + var l = sequence_length(i) + if (l > max) { + max = l + n = i + } +} + +return n; diff --git a/src/test/resources/ai/euler/pe015.leek b/src/test/resources/ai/euler/pe015.leek new file mode 100644 index 00000000..65145c4a --- /dev/null +++ b/src/test/resources/ai/euler/pe015.leek @@ -0,0 +1,22 @@ +var S = 20 + +var paths = [] +fill(paths, 0, 21) + +var walk = function(x, y) { + if (x + y == S) { + paths[y]++ + return; + } + if (x > 0) walk(x - 1, y) + if (y > 0) walk(x, y - 1) +} + +walk(S, S) + +var sum = 1 +for (var i = 0; i < S; ++i) { + var long = paths[i] * paths[i] + sum += long +} +return sum; diff --git a/src/test/resources/ai/euler/pe016.leek b/src/test/resources/ai/euler/pe016.leek new file mode 100644 index 00000000..d8af28ad --- /dev/null +++ b/src/test/resources/ai/euler/pe016.leek @@ -0,0 +1,4 @@ + (2m ** 1000).string().fold((x, y) -> x + y.number(), 0) + +// TODO : only 2 ** 1000 without the m -> detect that it's a large number constant +// TODO : fold the number itself (base 10 by default), iterate over digits as integers diff --git a/src/test/resources/ai/euler/pe017.leek b/src/test/resources/ai/euler/pe017.leek new file mode 100644 index 00000000..459261a1 --- /dev/null +++ b/src/test/resources/ai/euler/pe017.leek @@ -0,0 +1,54 @@ +function get(n) { + if (n == 1) return "one" + if (n == 2) return "two" + if (n == 3) return "three" + if (n == 4) return "four" + if (n == 5) return "five" + if (n == 6) return "six" + if (n == 7) return "seven" + if (n == 8) return "eight" + if (n == 9) return "nine" + if (n == 10) return "ten" + if (n == 11) return "eleven" + if (n == 12) return "twelve" + if (n == 13) return "thirteen" + if (n == 14) return "fourteen" + if (n == 15) return "fifteen" + if (n == 16) return "sixteen" + if (n == 17) return "seventeen" + if (n == 18) return "eighteen" + if (n == 19) return "nineteen" + + if (n == 20) return "twenty" + if (n == 30) return "thirty" + if (n == 40) return "forty" + if (n == 50) return "fifty" + if (n == 60) return "sixty" + if (n == 70) return "seventy" + if (n == 80) return "eighty" + if (n == 90) return "ninety" + + if (n < 100) { + var diz = (n / 10 | 0) * 10 + return get(diz) + get(n - diz) + } + + if (n < 1000) { + var cent = n / 100 | 0 + var reste = n % 100 + var res = get(cent) + "hundred" + if (reste > 0) { + // TODO res += + res = res + "and" + get(reste) + } + return res + } + return "onethousand" +} + +var sum = 0 +for (var i = 1; i <= 1000; ++i) { + sum += length(get(i)) +} + +return sum; \ No newline at end of file diff --git a/src/test/resources/ai/euler/pe018.leek b/src/test/resources/ai/euler/pe018.leek new file mode 100644 index 00000000..699f4e0a --- /dev/null +++ b/src/test/resources/ai/euler/pe018.leek @@ -0,0 +1,34 @@ + + +var tryRoute = function (level, x, sum, maxi) { + + var tri = [ + [75], + [95, 64], + [17, 47, 82], + [18, 35, 87, 10], + [20, 4, 82, 47, 65], + [19, 1, 23, 75, 3, 34], + [88, 2, 77, 73, 7, 63, 67], + [99, 65, 4, 28, 6, 16, 70, 92], + [41, 41, 26, 56, 83, 40, 80, 70, 33], + [41, 48, 72, 33, 47, 32, 37, 16, 94, 29], + [53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14], + [70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57], + [91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48], + [63, 66, 4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31], + [ 4, 62, 98, 27, 23, 9, 70, 98, 73, 93, 38, 53, 60, 4, 23] + ] + sum += tri[level][x] + + if (level == 14) { + if (sum > maxi) maxi = sum + return maxi + } else { + maxi = tryRoute(level + 1, x, sum, maxi) + return tryRoute(level + 1, x + 1, sum, maxi) + } +} + +return tryRoute(0, 0, 0, 0); + diff --git a/src/test/resources/ai/euler/pe019.leek b/src/test/resources/ai/euler/pe019.leek new file mode 100644 index 00000000..e9ddfd4b --- /dev/null +++ b/src/test/resources/ai/euler/pe019.leek @@ -0,0 +1,22 @@ +var day = 1 // 1 janvier 1901 => mardi +var sundays = 0 + +for (var i = 1901; i <= 2000; ++i) { + + var leap = (i % 100 != 0 && i % 4 == 0) || (i % 400 == 0) + + for (var m = 0; m < 12; ++m) { + + if (day % 7 == 6) sundays++ + + if (m == 3 || m == 5 || m == 8 || m == 10) { + day += 30 + } else if (m == 1) { + day += leap ? 29 : 28 + } else { + day += 31 + } + } +} + +return sundays; \ No newline at end of file diff --git a/src/test/resources/ai/euler/pe020.leek b/src/test/resources/ai/euler/pe020.leek new file mode 100644 index 00000000..bece303a --- /dev/null +++ b/src/test/resources/ai/euler/pe020.leek @@ -0,0 +1,12 @@ +// TODO create a builtin fact function +var fact = function(x) { x == 1 ? 1 : fact(x - 1) * x } + +var f = fact(100) +var s = f.string() // TODO mpz.string() directly + +var sum = 0 +for (var c in s) { + sum += c.code() - '0'.code() +} + +sum \ No newline at end of file diff --git a/src/test/resources/ai/euler/pe021.leek b/src/test/resources/ai/euler/pe021.leek new file mode 100644 index 00000000..f640f76e --- /dev/null +++ b/src/test/resources/ai/euler/pe021.leek @@ -0,0 +1,15 @@ +function sumDiv(n) { + var num = 0 + for (var i = 1; i < n; ++i) + if (n % i == 0) { + num += i + } + num +} + +function isAmicable(i) { + var sum = sumDiv(i) + sum != i && sumDiv(sum) == i +} + +[1..10000].filter(isAmicable).sum() \ No newline at end of file diff --git a/src/test/resources/ai/euler/pe022.leek b/src/test/resources/ai/euler/pe022.leek new file mode 100644 index 00000000..6a6f78d7 --- /dev/null +++ b/src/test/resources/ai/euler/pe022.leek @@ -0,0 +1,15 @@ +var names = "MARY PATRICIA LINDA BARBARA ELIZABETH JENNIFER MARIA SUSAN MARGARET DOROTHY LISA NANCY KAREN BETTY HELEN SANDRA DONNA CAROL RUTH SHARON MICHELLE LAURA SARAH KIMBERLY DEBORAH JESSICA SHIRLEY CYNTHIA ANGELA MELISSA BRENDA AMY ANNA REBECCA VIRGINIA KATHLEEN PAMELA MARTHA DEBRA AMANDA STEPHANIE CAROLYN CHRISTINE MARIE JANET CATHERINE FRANCES ANN JOYCE DIANE ALICE JULIE HEATHER TERESA DORIS GLORIA EVELYN JEAN CHERYL MILDRED KATHERINE JOAN ASHLEY JUDITH ROSE JANICE KELLY NICOLE JUDY CHRISTINA KATHY THERESA BEVERLY DENISE TAMMY IRENE JANE LORI RACHEL MARILYN ANDREA KATHRYN LOUISE SARA ANNE JACQUELINE WANDA BONNIE JULIA RUBY LOIS TINA PHYLLIS NORMA PAULA DIANA ANNIE LILLIAN EMILY ROBIN PEGGY CRYSTAL GLADYS RITA DAWN CONNIE FLORENCE TRACY EDNA TIFFANY CARMEN ROSA CINDY GRACE WENDY VICTORIA EDITH KIM SHERRY SYLVIA JOSEPHINE THELMA SHANNON SHEILA ETHEL ELLEN ELAINE MARJORIE CARRIE CHARLOTTE MONICA ESTHER PAULINE EMMA JUANITA ANITA RHONDA HAZEL AMBER EVA DEBBIE APRIL LESLIE CLARA LUCILLE JAMIE JOANNE ELEANOR VALERIE DANIELLE MEGAN ALICIA SUZANNE MICHELE GAIL BERTHA DARLENE VERONICA JILL ERIN GERALDINE LAUREN CATHY JOANN LORRAINE LYNN SALLY REGINA ERICA BEATRICE DOLORES BERNICE AUDREY YVONNE ANNETTE JUNE SAMANTHA MARION DANA STACY ANA RENEE IDA VIVIAN ROBERTA HOLLY BRITTANY MELANIE LORETTA YOLANDA JEANETTE LAURIE KATIE KRISTEN VANESSA ALMA SUE ELSIE BETH JEANNE VICKI CARLA TARA ROSEMARY EILEEN TERRI GERTRUDE LUCY TONYA ELLA STACEY WILMA GINA KRISTIN JESSIE NATALIE AGNES VERA WILLIE CHARLENE BESSIE DELORES MELINDA PEARL ARLENE MAUREEN COLLEEN ALLISON TAMARA JOY GEORGIA CONSTANCE LILLIE CLAUDIA JACKIE MARCIA TANYA NELLIE MINNIE MARLENE HEIDI GLENDA LYDIA VIOLA COURTNEY MARIAN STELLA CAROLINE DORA JO VICKIE MATTIE TERRY MAXINE IRMA MABEL MARSHA MYRTLE LENA CHRISTY DEANNA PATSY HILDA GWENDOLYN JENNIE NORA MARGIE NINA CASSANDRA LEAH PENNY KAY PRISCILLA NAOMI CAROLE BRANDY OLGA BILLIE DIANNE TRACEY LEONA JENNY FELICIA SONIA MIRIAM VELMA BECKY BOBBIE VIOLET KRISTINA TONI MISTY MAE SHELLY DAISY RAMONA SHERRI ERIKA KATRINA CLAIRE LINDSEY LINDSAY GENEVA GUADALUPE BELINDA MARGARITA SHERYL CORA FAYE ADA NATASHA SABRINA ISABEL MARGUERITE HATTIE HARRIET MOLLY CECILIA KRISTI BRANDI BLANCHE SANDY ROSIE JOANNA IRIS EUNICE ANGIE INEZ LYNDA MADELINE AMELIA ALBERTA GENEVIEVE MONIQUE JODI JANIE MAGGIE KAYLA SONYA JAN LEE KRISTINE CANDACE FANNIE MARYANN OPAL ALISON YVETTE MELODY LUZ SUSIE OLIVIA FLORA SHELLEY KRISTY MAMIE LULA LOLA VERNA BEULAH ANTOINETTE CANDICE JUANA JEANNETTE PAM KELLI HANNAH WHITNEY BRIDGET KARLA CELIA LATOYA PATTY SHELIA GAYLE DELLA VICKY LYNNE SHERI MARIANNE KARA JACQUELYN ERMA BLANCA MYRA LETICIA PAT KRISTA ROXANNE ANGELICA JOHNNIE ROBYN FRANCIS ADRIENNE ROSALIE ALEXANDRA BROOKE BETHANY SADIE BERNADETTE TRACI JODY KENDRA JASMINE NICHOLE RACHAEL CHELSEA MABLE ERNESTINE MURIEL MARCELLA ELENA KRYSTAL ANGELINA NADINE KARI ESTELLE DIANNA PAULETTE LORA MONA DOREEN ROSEMARIE ANGEL DESIREE ANTONIA HOPE GINGER JANIS BETSY CHRISTIE FREDA MERCEDES MEREDITH LYNETTE TERI CRISTINA EULA LEIGH MEGHAN SOPHIA ELOISE ROCHELLE GRETCHEN CECELIA RAQUEL HENRIETTA ALYSSA JANA KELLEY GWEN KERRY JENNA TRICIA LAVERNE OLIVE ALEXIS TASHA SILVIA ELVIRA CASEY DELIA SOPHIE KATE PATTI LORENA KELLIE SONJA LILA LANA DARLA MAY MINDY ESSIE MANDY LORENE ELSA JOSEFINA JEANNIE MIRANDA DIXIE LUCIA MARTA FAITH LELA JOHANNA SHARI CAMILLE TAMI SHAWNA ELISA EBONY MELBA ORA NETTIE TABITHA OLLIE JAIME WINIFRED KRISTIE MARINA ALISHA AIMEE RENA MYRNA MARLA TAMMIE LATASHA BONITA PATRICE RONDA SHERRIE ADDIE FRANCINE DELORIS STACIE ADRIANA CHERI SHELBY ABIGAIL CELESTE JEWEL CARA ADELE REBEKAH LUCINDA DORTHY CHRIS EFFIE TRINA REBA SHAWN SALLIE AURORA LENORA ETTA LOTTIE KERRI TRISHA NIKKI ESTELLA FRANCISCA JOSIE TRACIE MARISSA KARIN BRITTNEY JANELLE LOURDES LAUREL HELENE FERN ELVA CORINNE KELSEY INA BETTIE ELISABETH AIDA CAITLIN INGRID IVA EUGENIA CHRISTA GOLDIE CASSIE MAUDE JENIFER THERESE FRANKIE DENA LORNA JANETTE LATONYA CANDY MORGAN CONSUELO TAMIKA ROSETTA DEBORA CHERIE POLLY DINA JEWELL FAY JILLIAN DOROTHEA NELL TRUDY ESPERANZA PATRICA KIMBERLEY SHANNA HELENA CAROLINA CLEO STEFANIE ROSARIO OLA JANINE MOLLIE LUPE ALISA LOU MARIBEL SUSANNE BETTE SUSANA ELISE CECILE ISABELLE LESLEY JOCELYN PAIGE JONI RACHELLE LEOLA DAPHNE ALTA ESTER PETRA GRACIELA IMOGENE JOLENE KEISHA LACEY GLENNA GABRIELA KERI URSULA LIZZIE KIRSTEN SHANA ADELINE MAYRA JAYNE JACLYN GRACIE SONDRA CARMELA MARISA ROSALIND CHARITY TONIA BEATRIZ MARISOL CLARICE JEANINE SHEENA ANGELINE FRIEDA LILY ROBBIE SHAUNA MILLIE CLAUDETTE CATHLEEN ANGELIA GABRIELLE AUTUMN KATHARINE SUMMER JODIE STACI LEA CHRISTI JIMMIE JUSTINE ELMA LUELLA MARGRET DOMINIQUE SOCORRO RENE MARTINA MARGO MAVIS CALLIE BOBBI MARITZA LUCILE LEANNE JEANNINE DEANA AILEEN LORIE LADONNA WILLA MANUELA GALE SELMA DOLLY SYBIL ABBY LARA DALE IVY DEE WINNIE MARCY LUISA JERI MAGDALENA OFELIA MEAGAN AUDRA MATILDA LEILA CORNELIA BIANCA SIMONE BETTYE RANDI VIRGIE LATISHA BARBRA GEORGINA ELIZA LEANN BRIDGETTE RHODA HALEY ADELA NOLA BERNADINE FLOSSIE ILA GRETA RUTHIE NELDA MINERVA LILLY TERRIE LETHA HILARY ESTELA VALARIE BRIANNA ROSALYN EARLINE CATALINA AVA MIA CLARISSA LIDIA CORRINE ALEXANDRIA CONCEPCION TIA SHARRON RAE DONA ERICKA JAMI ELNORA CHANDRA LENORE NEVA MARYLOU MELISA TABATHA SERENA AVIS ALLIE SOFIA JEANIE ODESSA NANNIE HARRIETT LORAINE PENELOPE MILAGROS EMILIA BENITA ALLYSON ASHLEE TANIA TOMMIE ESMERALDA KARINA EVE PEARLIE ZELMA MALINDA NOREEN TAMEKA SAUNDRA HILLARY AMIE ALTHEA ROSALINDA JORDAN LILIA ALANA GAY CLARE ALEJANDRA ELINOR MICHAEL LORRIE JERRI DARCY EARNESTINE CARMELLA TAYLOR NOEMI MARCIE LIZA ANNABELLE LOUISA EARLENE MALLORY CARLENE NITA SELENA TANISHA KATY JULIANNE JOHN LAKISHA EDWINA MARICELA MARGERY KENYA DOLLIE ROXIE ROSLYN KATHRINE NANETTE CHARMAINE LAVONNE ILENE KRIS TAMMI SUZETTE CORINE KAYE JERRY MERLE CHRYSTAL LINA DEANNE LILIAN JULIANA ALINE LUANN KASEY MARYANNE EVANGELINE COLETTE MELVA LAWANDA YESENIA NADIA MADGE KATHIE EDDIE OPHELIA VALERIA NONA MITZI MARI GEORGETTE CLAUDINE FRAN ALISSA ROSEANN LAKEISHA SUSANNA REVA DEIDRE CHASITY SHEREE CARLY JAMES ELVIA ALYCE DEIRDRE GENA BRIANA ARACELI KATELYN ROSANNE WENDI TESSA BERTA MARVA IMELDA MARIETTA MARCI LEONOR ARLINE SASHA MADELYN JANNA JULIETTE DEENA AURELIA JOSEFA AUGUSTA LILIANA YOUNG CHRISTIAN LESSIE AMALIA SAVANNAH ANASTASIA VILMA NATALIA ROSELLA LYNNETTE CORINA ALFREDA LEANNA CAREY AMPARO COLEEN TAMRA AISHA WILDA KARYN CHERRY QUEEN MAURA MAI EVANGELINA ROSANNA HALLIE ERNA ENID MARIANA LACY JULIET JACKLYN FREIDA MADELEINE MARA HESTER CATHRYN LELIA CASANDRA BRIDGETT ANGELITA JANNIE DIONNE ANNMARIE KATINA BERYL PHOEBE MILLICENT KATHERYN DIANN CARISSA MARYELLEN LIZ LAURI HELGA GILDA ADRIAN RHEA MARQUITA HOLLIE TISHA TAMERA ANGELIQUE FRANCESCA BRITNEY KAITLIN LOLITA FLORINE ROWENA REYNA TWILA FANNY JANELL INES CONCETTA BERTIE ALBA BRIGITTE ALYSON VONDA PANSY ELBA NOELLE LETITIA KITTY DEANN BRANDIE LOUELLA LETA FELECIA SHARLENE LESA BEVERLEY ROBERT ISABELLA HERMINIA TERRA CELINA TORI OCTAVIA JADE DENICE GERMAINE SIERRA MICHELL CORTNEY NELLY DORETHA SYDNEY DEIDRA MONIKA LASHONDA JUDI CHELSEY ANTIONETTE MARGOT BOBBY ADELAIDE NAN LEEANN ELISHA DESSIE LIBBY KATHI GAYLA LATANYA MINA MELLISA KIMBERLEE JASMIN RENAE ZELDA ELDA MA JUSTINA GUSSIE EMILIE CAMILLA ABBIE ROCIO KAITLYN JESSE EDYTHE ASHLEIGH SELINA LAKESHA GERI ALLENE PAMALA MICHAELA DAYNA CARYN ROSALIA SUN JACQULINE REBECA MARYBETH KRYSTLE IOLA DOTTIE BENNIE BELLE AUBREY GRISELDA ERNESTINA ELIDA ADRIANNE DEMETRIA DELMA CHONG JAQUELINE DESTINY ARLEEN VIRGINA RETHA FATIMA TILLIE ELEANORE CARI TREVA BIRDIE WILHELMINA ROSALEE MAURINE LATRICE YONG JENA TARYN ELIA DEBBY MAUDIE JEANNA DELILAH CATRINA SHONDA HORTENCIA THEODORA TERESITA ROBBIN DANETTE MARYJANE FREDDIE DELPHINE BRIANNE NILDA DANNA CINDI BESS IONA HANNA ARIEL WINONA VIDA ROSITA MARIANNA WILLIAM RACHEAL GUILLERMINA ELOISA CELESTINE CAREN MALISSA LONA CHANTEL SHELLIE MARISELA LEORA AGATHA SOLEDAD MIGDALIA IVETTE CHRISTEN ATHENA JANEL CHLOE VEDA PATTIE TESSIE TERA MARILYNN LUCRETIA KARRIE DINAH DANIELA ALECIA ADELINA VERNICE SHIELA PORTIA MERRY LASHAWN DEVON DARA TAWANA OMA VERDA CHRISTIN ALENE ZELLA SANDI RAFAELA MAYA KIRA CANDIDA ALVINA SUZAN SHAYLA LYN LETTIE ALVA SAMATHA ORALIA MATILDE MADONNA LARISSA VESTA RENITA INDIA DELOIS SHANDA PHILLIS LORRI ERLINDA CRUZ CATHRINE BARB ZOE ISABELL IONE GISELA CHARLIE VALENCIA ROXANNA MAYME KISHA ELLIE MELLISSA DORRIS DALIA BELLA ANNETTA ZOILA RETA REINA LAURETTA KYLIE CHRISTAL PILAR CHARLA ELISSA TIFFANI TANA PAULINA LEOTA BREANNA JAYME CARMEL VERNELL TOMASA MANDI DOMINGA SANTA MELODIE LURA ALEXA TAMELA RYAN MIRNA KERRIE VENUS NOEL FELICITA CRISTY CARMELITA BERNIECE ANNEMARIE TIARA ROSEANNE MISSY CORI ROXANA PRICILLA KRISTAL JUNG ELYSE HAYDEE ALETHA BETTINA MARGE GILLIAN FILOMENA CHARLES ZENAIDA HARRIETTE CARIDAD VADA UNA ARETHA PEARLINE MARJORY MARCELA FLOR EVETTE ELOUISE ALINA TRINIDAD DAVID DAMARIS CATHARINE CARROLL BELVA NAKIA MARLENA LUANNE LORINE KARON DORENE DANITA BRENNA TATIANA SAMMIE LOUANN LOREN JULIANNA ANDRIA PHILOMENA LUCILA LEONORA DOVIE ROMONA MIMI JACQUELIN GAYE TONJA MISTI JOE GENE CHASTITY STACIA ROXANN MICAELA NIKITA MEI VELDA MARLYS JOHNNA AURA LAVERN IVONNE HAYLEY NICKI MAJORIE HERLINDA GEORGE ALPHA YADIRA PERLA GREGORIA DANIEL ANTONETTE SHELLI MOZELLE MARIAH JOELLE CORDELIA JOSETTE CHIQUITA TRISTA LOUIS LAQUITA GEORGIANA CANDI SHANON LONNIE HILDEGARD CECIL VALENTINA STEPHANY MAGDA KAROL GERRY GABRIELLA TIANA ROMA RICHELLE RAY PRINCESS OLETA JACQUE IDELLA ALAINA SUZANNA JOVITA BLAIR TOSHA RAVEN NEREIDA MARLYN KYLA JOSEPH DELFINA TENA STEPHENIE SABINA NATHALIE MARCELLE GERTIE DARLEEN THEA SHARONDA SHANTEL BELEN VENESSA ROSALINA ONA GENOVEVA COREY CLEMENTINE ROSALBA RENATE RENATA MI IVORY GEORGIANNA FLOY DORCAS ARIANA TYRA THEDA MARIAM JULI JESICA DONNIE VIKKI VERLA ROSELYN MELVINA JANNETTE GINNY DEBRAH CORRIE ASIA VIOLETA MYRTIS LATRICIA COLLETTE CHARLEEN ANISSA VIVIANA TWYLA PRECIOUS NEDRA LATONIA LAN HELLEN FABIOLA ANNAMARIE ADELL SHARYN CHANTAL NIKI MAUD LIZETTE LINDY KIA KESHA JEANA DANELLE CHARLINE CHANEL CARROL VALORIE LIA DORTHA CRISTAL SUNNY LEONE LEILANI GERRI DEBI ANDRA KESHIA IMA EULALIA EASTER DULCE NATIVIDAD LINNIE KAMI GEORGIE CATINA BROOK ALDA WINNIFRED SHARLA RUTHANN MEAGHAN MAGDALENE LISSETTE ADELAIDA VENITA TRENA SHIRLENE SHAMEKA ELIZEBETH DIAN SHANTA MICKEY LATOSHA CARLOTTA WINDY SOON ROSINA MARIANN LEISA JONNIE DAWNA CATHIE BILLY ASTRID SIDNEY LAUREEN JANEEN HOLLI FAWN VICKEY TERESSA SHANTE RUBYE MARCELINA CHANDA CARY TERESE SCARLETT MARTY MARNIE LULU LISETTE JENIFFER ELENOR DORINDA DONITA CARMAN BERNITA ALTAGRACIA ALETA ADRIANNA ZORAIDA RONNIE NICOLA LYNDSEY KENDALL JANINA CHRISSY AMI STARLA PHYLIS PHUONG KYRA CHARISSE BLANCH SANJUANITA RONA NANCI MARILEE MARANDA CORY BRIGETTE SANJUANA MARITA KASSANDRA JOYCELYN IRA FELIPA CHELSIE BONNY MIREYA LORENZA KYONG ILEANA CANDELARIA TONY TOBY SHERIE OK MARK LUCIE LEATRICE LAKESHIA GERDA EDIE BAMBI MARYLIN LAVON HORTENSE GARNET EVIE TRESSA SHAYNA LAVINA KYUNG JEANETTA SHERRILL SHARA PHYLISS MITTIE ANABEL ALESIA THUY TAWANDA RICHARD JOANIE TIFFANIE LASHANDA KARISSA ENRIQUETA DARIA DANIELLA CORINNA ALANNA ABBEY ROXANE ROSEANNA MAGNOLIA LIDA KYLE JOELLEN ERA CORAL CARLEEN TRESA PEGGIE NOVELLA NILA MAYBELLE JENELLE CARINA NOVA MELINA MARQUERITE MARGARETTE JOSEPHINA EVONNE DEVIN CINTHIA ALBINA TOYA TAWNYA SHERITA SANTOS MYRIAM LIZABETH LISE KEELY JENNI GISELLE CHERYLE ARDITH ARDIS ALESHA ADRIANE SHAINA LINNEA KAROLYN HONG FLORIDA FELISHA DORI DARCI ARTIE ARMIDA ZOLA XIOMARA VERGIE SHAMIKA NENA NANNETTE MAXIE LOVIE JEANE JAIMIE INGE FARRAH ELAINA CAITLYN STARR FELICITAS CHERLY CARYL YOLONDA YASMIN TEENA PRUDENCE PENNIE NYDIA MACKENZIE ORPHA MARVEL LIZBETH LAURETTE JERRIE HERMELINDA CAROLEE TIERRA MIRIAN META MELONY KORI JENNETTE JAMILA ENA ANH YOSHIKO SUSANNAH SALINA RHIANNON JOLEEN CRISTINE ASHTON ARACELY TOMEKA SHALONDA MARTI LACIE KALA JADA ILSE HAILEY BRITTANI ZONA SYBLE SHERRYL RANDY NIDIA MARLO KANDICE KANDI DEB DEAN AMERICA ALYCIA TOMMY RONNA NORENE MERCY JOSE INGEBORG GIOVANNA GEMMA CHRISTEL AUDRY ZORA VITA VAN TRISH STEPHAINE SHIRLEE SHANIKA MELONIE MAZIE JAZMIN INGA HOA HETTIE GERALYN FONDA ESTRELLA ADELLA SU SARITA RINA MILISSA MARIBETH GOLDA EVON ETHELYN ENEDINA CHERISE CHANA VELVA TAWANNA SADE MIRTA LI KARIE JACINTA ELNA DAVINA CIERRA ASHLIE ALBERTHA TANESHA STEPHANI NELLE MINDI LU LORINDA LARUE FLORENE DEMETRA DEDRA CIARA CHANTELLE ASHLY SUZY ROSALVA NOELIA LYDA LEATHA KRYSTYNA KRISTAN KARRI DARLINE DARCIE CINDA CHEYENNE CHERRIE AWILDA ALMEDA ROLANDA LANETTE JERILYN GISELE EVALYN CYNDI CLETA CARIN ZINA ZENA VELIA TANIKA PAUL CHARISSA THOMAS TALIA MARGARETE LAVONDA KAYLEE KATHLENE JONNA IRENA ILONA IDALIA CANDIS CANDANCE BRANDEE ANITRA ALIDA SIGRID NICOLETTE MARYJO LINETTE HEDWIG CHRISTIANA CASSIDY ALEXIA TRESSIE MODESTA LUPITA LITA GLADIS EVELIA DAVIDA CHERRI CECILY ASHELY ANNABEL AGUSTINA WANITA SHIRLY ROSAURA HULDA EUN BAILEY YETTA VERONA THOMASINA SIBYL SHANNAN MECHELLE LUE LEANDRA LANI KYLEE KANDY JOLYNN FERNE EBONI CORENE ALYSIA ZULA NADA MOIRA LYNDSAY LORRETTA JUAN JAMMIE HORTENSIA GAYNELL CAMERON ADRIA VINA VICENTA TANGELA STEPHINE NORINE NELLA LIANA LESLEE KIMBERELY ILIANA GLORY FELICA EMOGENE ELFRIEDE EDEN EARTHA CARMA BEA OCIE MARRY LENNIE KIARA JACALYN CARLOTA ARIELLE YU STAR OTILIA KIRSTIN KACEY JOHNETTA JOEY JOETTA JERALDINE JAUNITA ELANA DORTHEA CAMI AMADA ADELIA VERNITA TAMAR SIOBHAN RENEA RASHIDA OUIDA ODELL NILSA MERYL KRISTYN JULIETA DANICA BREANNE AUREA ANGLEA SHERRON ODETTE MALIA LORELEI LIN LEESA KENNA KATHLYN FIONA CHARLETTE SUZIE SHANTELL SABRA RACQUEL MYONG MIRA MARTINE LUCIENNE LAVADA JULIANN JOHNIE ELVERA DELPHIA CLAIR CHRISTIANE CHAROLETTE CARRI AUGUSTINE ASHA ANGELLA PAOLA NINFA LEDA LAI EDA SUNSHINE STEFANI SHANELL PALMA MACHELLE LISSA KECIA KATHRYNE KARLENE JULISSA JETTIE JENNIFFER HUI CORRINA CHRISTOPHER CAROLANN ALENA TESS ROSARIA MYRTICE MARYLEE LIANE KENYATTA JUDIE JANEY IN ELMIRA ELDORA DENNA CRISTI CATHI ZAIDA VONNIE VIVA VERNIE ROSALINE MARIELA LUCIANA LESLI KARAN FELICE DENEEN ADINA WYNONA TARSHA SHERON SHASTA SHANITA SHANI SHANDRA RANDA PINKIE PARIS NELIDA MARILOU LYLA LAURENE LACI JOI JANENE DOROTHA DANIELE DANI CAROLYNN CARLYN BERENICE AYESHA ANNELIESE ALETHEA THERSA TAMIKO RUFINA OLIVA MOZELL MARYLYN MADISON KRISTIAN KATHYRN KASANDRA KANDACE JANAE GABRIEL DOMENICA DEBBRA DANNIELLE CHUN BUFFY BARBIE ARCELIA AJA ZENOBIA SHAREN SHAREE PATRICK PAGE MY LAVINIA KUM KACIE JACKELINE HUONG FELISA EMELIA ELEANORA CYTHIA CRISTIN CLYDE CLARIBEL CARON ANASTACIA ZULMA ZANDRA YOKO TENISHA SUSANN SHERILYN SHAY SHAWANDA SABINE ROMANA MATHILDA LINSEY KEIKO JOANA ISELA GRETTA GEORGETTA EUGENIE DUSTY DESIRAE DELORA CORAZON ANTONINA ANIKA WILLENE TRACEE TAMATHA REGAN NICHELLE MICKIE MAEGAN LUANA LANITA KELSIE EDELMIRA BREE AFTON TEODORA TAMIE SHENA MEG LINH KELI KACI DANYELLE BRITT ARLETTE ALBERTINE ADELLE TIFFINY STORMY SIMONA NUMBERS NICOLASA NICHOL NIA NAKISHA MEE MAIRA LOREEN KIZZY JOHNNY JAY FALLON CHRISTENE BOBBYE ANTHONY YING VINCENZA TANJA RUBIE RONI QUEENIE MARGARETT KIMBERLI IRMGARD IDELL HILMA EVELINA ESTA EMILEE DENNISE DANIA CARL CARIE ANTONIO WAI SANG RISA RIKKI PARTICIA MUI MASAKO MARIO LUVENIA LOREE LONI LIEN KEVIN GIGI FLORENCIA DORIAN DENITA DALLAS CHI BILLYE ALEXANDER TOMIKA SHARITA RANA NIKOLE NEOMA MARGARITE MADALYN LUCINA LAILA KALI JENETTE GABRIELE EVELYNE ELENORA CLEMENTINA ALEJANDRINA ZULEMA VIOLETTE VANNESSA THRESA RETTA PIA PATIENCE NOELLA NICKIE JONELL DELTA CHUNG CHAYA CAMELIA BETHEL ANYA ANDREW THANH SUZANN SPRING SHU MILA LILLA LAVERNA KEESHA KATTIE GIA GEORGENE EVELINE ESTELL ELIZBETH VIVIENNE VALLIE TRUDIE STEPHANE MICHEL MAGALY MADIE KENYETTA KARREN JANETTA HERMINE HARMONY DRUCILLA DEBBI CELESTINA CANDIE BRITNI BECKIE AMINA ZITA YUN YOLANDE VIVIEN VERNETTA TRUDI SOMMER PEARLE PATRINA OSSIE NICOLLE LOYCE LETTY LARISA KATHARINA JOSELYN JONELLE JENELL IESHA HEIDE FLORINDA FLORENTINA FLO ELODIA DORINE BRUNILDA BRIGID ASHLI ARDELLA TWANA THU TARAH SUNG SHEA SHAVON SHANE SERINA RAYNA RAMONITA NGA MARGURITE LUCRECIA KOURTNEY KATI JESUS JESENIA DIAMOND CRISTA AYANA ALICA ALIA VINNIE SUELLEN ROMELIA RACHELL PIPER OLYMPIA MICHIKO KATHALEEN JOLIE JESSI JANESSA HANA HA ELEASE CARLETTA BRITANY SHONA SALOME ROSAMOND REGENA RAINA NGOC NELIA LOUVENIA LESIA LATRINA LATICIA LARHONDA JINA JACKI HOLLIS HOLLEY EMMY DEEANN CORETTA ARNETTA VELVET THALIA SHANICE NETA MIKKI MICKI LONNA LEANA LASHUNDA KILEY JOYE JACQULYN IGNACIA HYUN HIROKO HENRY HENRIETTE ELAYNE DELINDA DARNELL DAHLIA COREEN CONSUELA CONCHITA CELINE BABETTE AYANNA ANETTE ALBERTINA SKYE SHAWNEE SHANEKA QUIANA PAMELIA MIN MERRI MERLENE MARGIT KIESHA KIERA KAYLENE JODEE JENISE ERLENE EMMIE ELSE DARYL DALILA DAISEY CODY CASIE BELIA BABARA VERSIE VANESA SHELBA SHAWNDA SAM NORMAN NIKIA NAOMA MARNA MARGERET MADALINE LAWANA KINDRA JUTTA JAZMINE JANETT HANNELORE GLENDORA GERTRUD GARNETT FREEDA FREDERICA FLORANCE FLAVIA DENNIS CARLINE BEVERLEE ANJANETTE VALDA TRINITY TAMALA STEVIE SHONNA SHA SARINA ONEIDA MICAH MERILYN MARLEEN LURLINE LENNA KATHERIN JIN JENI HAE GRACIA GLADY FARAH ERIC ENOLA EMA DOMINQUE DEVONA DELANA CECILA CAPRICE ALYSHA ALI ALETHIA VENA THERESIA TAWNY SONG SHAKIRA SAMARA SACHIKO RACHELE PAMELLA NICKY MARNI MARIEL MAREN MALISA LIGIA LERA LATORIA LARAE KIMBER KATHERN KAREY JENNEFER JANETH HALINA FREDIA DELISA DEBROAH CIERA CHIN ANGELIKA ANDREE ALTHA YEN VIVAN TERRESA TANNA SUK SUDIE SOO SIGNE SALENA RONNI REBBECCA MYRTIE MCKENZIE MALIKA MAIDA LOAN LEONARDA KAYLEIGH FRANCE ETHYL ELLYN DAYLE CAMMIE BRITTNI BIRGIT AVELINA ASUNCION ARIANNA AKIKO VENICE TYESHA TONIE TIESHA TAKISHA STEFFANIE SINDY SANTANA MEGHANN MANDA MACIE LADY KELLYE KELLEE JOSLYN JASON INGER INDIRA GLINDA GLENNIS FERNANDA FAUSTINA ENEIDA ELICIA DOT DIGNA DELL ARLETTA ANDRE WILLIA TAMMARA TABETHA SHERRELL SARI REFUGIO REBBECA PAULETTA NIEVES NATOSHA NAKITA MAMMIE KENISHA KAZUKO KASSIE GARY EARLEAN DAPHINE CORLISS CLOTILDE CAROLYNE BERNETTA AUGUSTINA AUDREA ANNIS ANNABELL YAN TENNILLE TAMICA SELENE SEAN ROSANA REGENIA QIANA MARKITA MACY LEEANNE LAURINE KYM JESSENIA JANITA GEORGINE GENIE EMIKO ELVIE DEANDRA DAGMAR CORIE COLLEN CHERISH ROMAINE PORSHA PEARLENE MICHELINE MERNA MARGORIE MARGARETTA LORE KENNETH JENINE HERMINA FREDERICKA ELKE DRUSILLA DORATHY DIONE DESIRE CELENA BRIGIDA ANGELES ALLEGRA THEO TAMEKIA SYNTHIA STEPHEN SOOK SLYVIA ROSANN REATHA RAYE MARQUETTA MARGART LING LAYLA KYMBERLY KIANA KAYLEEN KATLYN KARMEN JOELLA IRINA EMELDA ELENI DETRA CLEMMIE CHERYLL CHANTELL CATHEY ARNITA ARLA ANGLE ANGELIC ALYSE ZOFIA THOMASINE TENNIE SON SHERLY SHERLEY SHARYL REMEDIOS PETRINA NICKOLE MYUNG MYRLE MOZELLA LOUANNE LISHA LATIA LANE KRYSTA JULIENNE JOEL JEANENE JACQUALINE ISAURA GWENDA EARLEEN DONALD CLEOPATRA CARLIE AUDIE ANTONIETTA ALISE ALEX VERDELL VAL TYLER TOMOKO THAO TALISHA STEVEN SO SHEMIKA SHAUN SCARLET SAVANNA SANTINA ROSIA RAEANN ODILIA NANA MINNA MAGAN LYNELLE LE KARMA JOEANN IVANA INELL ILANA HYE HONEY HEE GUDRUN FRANK DREAMA CRISSY CHANTE CARMELINA ARVILLA ARTHUR ANNAMAE ALVERA ALEIDA AARON YEE YANIRA VANDA TIANNA TAM STEFANIA SHIRA PERRY NICOL NANCIE MONSERRATE MINH MELYNDA MELANY MATTHEW LOVELLA LAURE KIRBY KACY JACQUELYNN HYON GERTHA FRANCISCO ELIANA CHRISTENA CHRISTEEN CHARISE CATERINA CARLEY CANDYCE ARLENA AMMIE YANG WILLETTE VANITA TUYET TINY SYREETA SILVA SCOTT RONALD PENNEY NYLA MICHAL MAURICE MARYAM MARYA MAGEN LUDIE LOMA LIVIA LANELL KIMBERLIE JULEE DONETTA DIEDRA DENISHA DEANE DAWNE CLARINE CHERRYL BRONWYN BRANDON ALLA VALERY TONDA SUEANN SORAYA SHOSHANA SHELA SHARLEEN SHANELLE NERISSA MICHEAL MERIDITH MELLIE MAYE MAPLE MAGARET LUIS LILI LEONILA LEONIE LEEANNA LAVONIA LAVERA KRISTEL KATHEY KATHE JUSTIN JULIAN JIMMY JANN ILDA HILDRED HILDEGARDE GENIA FUMIKO EVELIN ERMELINDA ELLY DUNG DOLORIS DIONNA DANAE BERNEICE ANNICE ALIX VERENA VERDIE TRISTAN SHAWNNA SHAWANA SHAUNNA ROZELLA RANDEE RANAE MILAGRO LYNELL LUISE LOUIE LOIDA LISBETH KARLEEN JUNITA JONA ISIS HYACINTH HEDY GWENN ETHELENE ERLINE EDWARD DONYA DOMONIQUE DELICIA DANNETTE CICELY BRANDA BLYTHE BETHANN ASHLYN ANNALEE ALLINE YUKO VELLA TRANG TOWANDA TESHA SHERLYN NARCISA MIGUELINA MERI MAYBELL MARLANA MARGUERITA MADLYN LUNA LORY LORIANN LIBERTY LEONORE LEIGHANN LAURICE LATESHA LARONDA KATRICE KASIE KARL KALEY JADWIGA GLENNIE GEARLDINE FRANCINA EPIFANIA DYAN DORIE DIEDRE DENESE DEMETRICE DELENA DARBY CRISTIE CLEORA CATARINA CARISA BERNIE BARBERA ALMETA TRULA TEREASA SOLANGE SHEILAH SHAVONNE SANORA ROCHELL MATHILDE MARGARETA MAIA LYNSEY LAWANNA LAUNA KENA KEENA KATIA JAMEY GLYNDA GAYLENE ELVINA ELANOR DANUTA DANIKA CRISTEN CORDIE COLETTA CLARITA CARMON BRYNN AZUCENA AUNDREA ANGELE YI WALTER VERLIE VERLENE TAMESHA SILVANA SEBRINA SAMIRA REDA RAYLENE PENNI PANDORA NORAH NOMA MIREILLE MELISSIA MARYALICE LARAINE KIMBERY KARYL KARINE KAM JOLANDA JOHANA JESUSA JALEESA JAE JACQUELYNE IRISH ILUMINADA HILARIA HANH GENNIE FRANCIE FLORETTA EXIE EDDA DREMA DELPHA BEV BARBAR ASSUNTA ARDELL ANNALISA ALISIA YUKIKO YOLANDO WONDA WEI WALTRAUD VETA TEQUILA TEMEKA TAMEIKA SHIRLEEN SHENITA PIEDAD OZELLA MIRTHA MARILU KIMIKO JULIANE JENICE JEN JANAY JACQUILINE HILDE FE FAE EVAN EUGENE ELOIS ECHO DEVORAH CHAU BRINDA BETSEY ARMINDA ARACELIS APRYL ANNETT ALISHIA VEOLA USHA TOSHIKO THEOLA TASHIA TALITHA SHERY RUDY RENETTA REIKO RASHEEDA OMEGA OBDULIA MIKA MELAINE MEGGAN MARTIN MARLEN MARGET MARCELINE MANA MAGDALEN LIBRADA LEZLIE LEXIE LATASHIA LASANDRA KELLE ISIDRA ISA INOCENCIA GWYN FRANCOISE ERMINIA ERINN DIMPLE DEVORA CRISELDA ARMANDA ARIE ARIANE ANGELO ANGELENA ALLEN ALIZA ADRIENE ADALINE XOCHITL TWANNA TRAN TOMIKO TAMISHA TAISHA SUSY SIU RUTHA ROXY RHONA RAYMOND OTHA NORIKO NATASHIA MERRIE MELVIN MARINDA MARIKO MARGERT LORIS LIZZETTE LEISHA KAILA KA JOANNIE JERRICA JENE JANNET JANEE JACINDA HERTA ELENORE DORETTA DELAINE DANIELL CLAUDIE CHINA BRITTA APOLONIA AMBERLY ALEASE YURI YUK WEN WANETA UTE TOMI SHARRI SANDIE ROSELLE REYNALDA RAGUEL PHYLICIA PATRIA OLIMPIA ODELIA MITZIE MITCHELL MISS MINDA MIGNON MICA MENDY MARIVEL MAILE LYNETTA LAVETTE LAURYN LATRISHA LAKIESHA KIERSTEN KARY JOSPHINE JOLYN JETTA JANISE JACQUIE IVELISSE GLYNIS GIANNA GAYNELLE EMERALD DEMETRIUS DANYELL DANILLE DACIA CORALEE CHER CEOLA BRETT BELL ARIANNE ALESHIA YUNG WILLIEMAE TROY TRINH THORA TAI SVETLANA SHERIKA SHEMEKA SHAUNDA ROSELINE RICKI MELDA MALLIE LAVONNA LATINA LARRY LAQUANDA LALA LACHELLE KLARA KANDIS JOHNA JEANMARIE JAYE HANG GRAYCE GERTUDE EMERITA EBONIE CLORINDA CHING CHERY CAROLA BREANN BLOSSOM BERNARDINE BECKI ARLETHA ARGELIA ARA ALITA YULANDA YON YESSENIA TOBI TASIA SYLVIE SHIRL SHIRELY SHERIDAN SHELLA SHANTELLE SACHA ROYCE REBECKA REAGAN PROVIDENCIA PAULENE MISHA MIKI MARLINE MARICA LORITA LATOYIA LASONYA KERSTIN KENDA KEITHA KATHRIN JAYMIE JACK GRICELDA GINETTE ERYN ELINA ELFRIEDA DANYEL CHEREE CHANELLE BARRIE AVERY AURORE ANNAMARIA ALLEEN AILENE AIDE YASMINE VASHTI VALENTINE TREASA TORY TIFFANEY SHERYLL SHARIE SHANAE SAU RAISA PA NEDA MITSUKO MIRELLA MILDA MARYANNA MARAGRET MABELLE LUETTA LORINA LETISHA LATARSHA LANELLE LAJUANA KRISSY KARLY KARENA JON JESSIKA JERICA JEANELLE JANUARY JALISA JACELYN IZOLA IVEY GREGORY EUNA ETHA DREW DOMITILA DOMINICA DAINA CREOLA CARLI CAMIE BUNNY BRITTNY ASHANTI ANISHA ALEEN ADAH YASUKO WINTER VIKI VALRIE TONA TINISHA THI TERISA TATUM TANEKA SIMONNE SHALANDA SERITA RESSIE REFUGIA PAZ OLENE NA MERRILL MARGHERITA MANDIE MAN MAIRE LYNDIA LUCI LORRIANE LORETA LEONIA LAVONA LASHAWNDA LAKIA KYOKO KRYSTINA KRYSTEN KENIA KELSI JUDE JEANICE ISOBEL GEORGIANN GENNY FELICIDAD EILENE DEON DELOISE DEEDEE DANNIE CONCEPTION CLORA CHERILYN CHANG CALANDRA BERRY ARMANDINA ANISA ULA TIMOTHY TIERA THERESSA STEPHANIA SIMA SHYLA SHONTA SHERA SHAQUITA SHALA SAMMY ROSSANA NOHEMI NERY MORIAH MELITA MELIDA MELANI MARYLYNN MARISHA MARIETTE MALORIE MADELENE LUDIVINA LORIA LORETTE LORALEE LIANNE LEON LAVENIA LAURINDA LASHON KIT KIMI KEILA KATELYNN KAI JONE JOANE JI JAYNA JANELLA JA HUE HERTHA FRANCENE ELINORE DESPINA DELSIE DEEDRA CLEMENCIA CARRY CAROLIN CARLOS BULAH BRITTANIE BOK BLONDELL BIBI BEAULAH BEATA ANNITA AGRIPINA VIRGEN VALENE UN TWANDA TOMMYE TOI TARRA TARI TAMMERA SHAKIA SADYE RUTHANNE ROCHEL RIVKA PURA NENITA NATISHA MING MERRILEE MELODEE MARVIS LUCILLA LEENA LAVETA LARITA LANIE KEREN ILEEN GEORGEANN GENNA GENESIS FRIDA EWA EUFEMIA EMELY ELA EDYTH DEONNA DEADRA DARLENA CHANELL CHAN CATHERN CASSONDRA CASSAUNDRA BERNARDA BERNA ARLINDA ANAMARIA ALBERT WESLEY VERTIE VALERI TORRI TATYANA STASIA SHERISE SHERILL SEASON SCOTTIE SANDA RUTHE ROSY ROBERTO ROBBI RANEE QUYEN PEARLY PALMIRA ONITA NISHA NIESHA NIDA NEVADA NAM MERLYN MAYOLA MARYLOUISE MARYLAND MARX MARTH MARGENE MADELAINE LONDA LEONTINE LEOMA LEIA LAWRENCE LAURALEE LANORA LAKITA KIYOKO KETURAH KATELIN KAREEN JONIE JOHNETTE JENEE JEANETT IZETTA HIEDI HEIKE HASSIE HAROLD GIUSEPPINA GEORGANN FIDELA FERNANDE ELWANDA ELLAMAE ELIZ DUSTI DOTTY CYNDY CORALIE CELESTA ARGENTINA ALVERTA XENIA WAVA VANETTA TORRIE TASHINA TANDY TAMBRA TAMA STEPANIE SHILA SHAUNTA SHARAN SHANIQUA SHAE SETSUKO SERAFINA SANDEE ROSAMARIA PRISCILA OLINDA NADENE MUOI MICHELINA MERCEDEZ MARYROSE MARIN MARCENE MAO MAGALI MAFALDA LOGAN LINN LANNIE KAYCE KAROLINE KAMILAH KAMALA JUSTA JOLINE JENNINE JACQUETTA IRAIDA GERALD GEORGEANNA FRANCHESCA FAIRY EMELINE ELANE EHTEL EARLIE DULCIE DALENE CRIS CLASSIE CHERE CHARIS CAROYLN CARMINA CARITA BRIAN BETHANIE AYAKO ARICA AN ALYSA ALESSANDRA AKILAH ADRIEN ZETTA YOULANDA YELENA YAHAIRA XUAN WENDOLYN VICTOR TIJUANA TERRELL TERINA TERESIA SUZI SUNDAY SHERELL SHAVONDA SHAUNTE SHARDA SHAKITA SENA RYANN RUBI RIVA REGINIA REA RACHAL PARTHENIA PAMULA MONNIE MONET MICHAELE MELIA MARINE MALKA MAISHA LISANDRA LEO LEKISHA LEAN LAURENCE LAKENDRA KRYSTIN KORTNEY KIZZIE KITTIE KERA KENDAL KEMBERLY KANISHA JULENE JULE JOSHUA JOHANNE JEFFREY JAMEE HAN HALLEY GIDGET GALINA FREDRICKA FLETA FATIMAH EUSEBIA ELZA ELEONORE DORTHEY DORIA DONELLA DINORAH DELORSE CLARETHA CHRISTINIA CHARLYN BONG BELKIS AZZIE ANDERA AIKO ADENA YER YAJAIRA WAN VANIA ULRIKE TOSHIA TIFANY STEFANY SHIZUE SHENIKA SHAWANNA SHAROLYN SHARILYN SHAQUANA SHANTAY SEE ROZANNE ROSELEE RICKIE REMONA REANNA RAELENE QUINN PHUNG PETRONILA NATACHA NANCEY MYRL MIYOKO MIESHA MERIDETH MARVELLA MARQUITTA MARHTA MARCHELLE LIZETH LIBBIE LAHOMA LADAWN KINA KATHELEEN KATHARYN KARISA KALEIGH JUNIE JULIEANN JOHNSIE JANEAN JAIMEE JACKQUELINE HISAKO HERMA HELAINE GWYNETH GLENN GITA EUSTOLIA EMELINA ELIN EDRIS DONNETTE DONNETTA DIERDRE DENAE DARCEL CLAUDE CLARISA CINDERELLA CHIA CHARLESETTA CHARITA CELSA CASSY CASSI CARLEE BRUNA BRITTANEY BRANDE BILLI BAO ANTONETTA ANGLA ANGELYN ANALISA ALANE WENONA WENDIE VERONIQUE VANNESA TOBIE TEMPIE SUMIKO SULEMA SPARKLE SOMER SHEBA SHAYNE SHARICE SHANEL SHALON SAGE ROY ROSIO ROSELIA RENAY REMA REENA PORSCHE PING PEG OZIE ORETHA ORALEE ODA NU NGAN NAKESHA MILLY MARYBELLE MARLIN MARIS MARGRETT MARAGARET MANIE LURLENE LILLIA LIESELOTTE LAVELLE LASHAUNDA LAKEESHA KEITH KAYCEE KALYN JOYA JOETTE JENAE JANIECE ILLA GRISEL GLAYDS GENEVIE GALA FREDDA FRED ELMER ELEONOR DEBERA DEANDREA DAN CORRINNE CORDIA CONTESSA COLENE CLEOTILDE CHARLOTT CHANTAY CECILLE BEATRIS AZALEE ARLEAN ARDATH ANJELICA ANJA ALFREDIA ALEISHA ADAM ZADA YUONNE XIAO WILLODEAN WHITLEY VENNIE VANNA TYISHA TOVA TORIE TONISHA TILDA TIEN TEMPLE SIRENA SHERRIL SHANTI SHAN SENAIDA SAMELLA ROBBYN RENDA REITA PHEBE PAULITA NOBUKO NGUYET NEOMI MOON MIKAELA MELANIA MAXIMINA MARG MAISIE LYNNA LILLI LAYNE LASHAUN LAKENYA LAEL KIRSTIE KATHLINE KASHA KARLYN KARIMA JOVAN JOSEFINE JENNELL JACQUI JACKELYN HYO HIEN GRAZYNA FLORRIE FLORIA ELEONORA DWANA DORLA DONG DELMY DEJA DEDE DANN CRYSTA CLELIA CLARIS CLARENCE CHIEKO CHERLYN CHERELLE CHARMAIN CHARA CAMMY BEE ARNETTE ARDELLE ANNIKA AMIEE AMEE ALLENA YVONE YUKI YOSHIE YEVETTE YAEL WILLETTA VONCILE VENETTA TULA TONETTE TIMIKA TEMIKA TELMA TEISHA TAREN TA STACEE SHIN SHAWNTA SATURNINA RICARDA POK PASTY ONIE NUBIA MORA MIKE MARIELLE MARIELLA MARIANELA MARDELL MANY LUANNA LOISE LISABETH LINDSY LILLIANA LILLIAM LELAH LEIGHA LEANORA LANG KRISTEEN KHALILAH KEELEY KANDRA JUNKO JOAQUINA JERLENE JANI JAMIKA JAME HSIU HERMILA GOLDEN GENEVIVE EVIA EUGENA EMMALINE ELFREDA ELENE DONETTE DELCIE DEEANNA DARCEY CUC CLARINDA CIRA CHAE CELINDA CATHERYN CATHERIN CASIMIRA CARMELIA CAMELLIA BREANA BOBETTE BERNARDINA BEBE BASILIA ARLYNE AMAL ALAYNA ZONIA ZENIA YURIKO YAEKO WYNELL WILLOW WILLENA VERNIA TU TRAVIS TORA TERRILYN TERICA TENESHA TAWNA TAJUANA TAINA STEPHNIE SONA SOL SINA SHONDRA SHIZUKO SHERLENE SHERICE SHARIKA ROSSIE ROSENA RORY RIMA RIA RHEBA RENNA PETER NATALYA NANCEE MELODI MEDA MAXIMA MATHA MARKETTA MARICRUZ MARCELENE MALVINA LUBA LOUETTA LEIDA LECIA LAURAN LASHAWNA LAINE KHADIJAH KATERINE KASI KALLIE JULIETTA JESUSITA JESTINE JESSIA JEREMY JEFFIE JANYCE ISADORA GEORGIANNE FIDELIA EVITA EURA EULAH ESTEFANA ELSY ELIZABET ELADIA DODIE DION DIA DENISSE DELORAS DELILA DAYSI DAKOTA CURTIS CRYSTLE CONCHA COLBY CLARETTA CHU CHRISTIA CHARLSIE CHARLENA CARYLON BETTYANN ASLEY ASHLEA AMIRA AI AGUEDA AGNUS YUETTE VINITA VICTORINA TYNISHA TREENA TOCCARA TISH THOMASENA TEGAN SOILA SHILOH SHENNA SHARMAINE SHANTAE SHANDI SEPTEMBER SARAN SARAI SANA SAMUEL SALLEY ROSETTE ROLANDE REGINE OTELIA OSCAR OLEVIA NICHOLLE NECOLE NAIDA MYRTA MYESHA MITSUE MINTA MERTIE MARGY MAHALIA MADALENE LOVE LOURA LOREAN LEWIS LESHA LEONIDA LENITA LAVONE LASHELL LASHANDRA LAMONICA KIMBRA KATHERINA KARRY KANESHA JULIO JONG JENEVA JAQUELYN HWA GILMA GHISLAINE GERTRUDIS FRANSISCA FERMINA ETTIE ETSUKO ELLIS ELLAN ELIDIA EDRA DORETHEA DOREATHA DENYSE DENNY DEETTA DAINE CYRSTAL CORRIN CAYLA CARLITA CAMILA BURMA BULA BUENA BLAKE BARABARA AVRIL AUSTIN ALAINE ZANA WILHEMINA WANETTA VIRGIL VI VERONIKA VERNON VERLINE VASILIKI TONITA TISA TEOFILA TAYNA TAUNYA TANDRA TAKAKO SUNNI SUANNE SIXTA SHARELL SEEMA RUSSELL ROSENDA ROBENA RAYMONDE PEI PAMILA OZELL NEIDA NEELY MISTIE MICHA MERISSA MAURITA MARYLN MARYETTA MARSHALL MARCELL MALENA MAKEDA MADDIE LOVETTA LOURIE LORRINE LORILEE LESTER LAURENA LASHAY LARRAINE LAREE LACRESHA KRISTLE KRISHNA KEVA KEIRA KAROLE JOIE JINNY JEANNETTA JAMA HEIDY GILBERTE GEMA FAVIOLA EVELYNN ENDA ELLI ELLENA DIVINA DAGNY COLLENE CODI CINDIE CHASSIDY CHASIDY CATRICE CATHERINA CASSEY CAROLL CARLENA CANDRA CALISTA BRYANNA BRITTENY BEULA BARI AUDRIE AUDRIA ARDELIA ANNELLE ANGILA ALONA ALLYN DOUGLAS ROGER JONATHAN RALPH NICHOLAS BENJAMIN BRUCE HARRY WAYNE STEVE HOWARD ERNEST PHILLIP TODD CRAIG ALAN PHILIP EARL DANNY BRYAN STANLEY LEONARD NATHAN MANUEL RODNEY MARVIN VINCENT JEFFERY JEFF CHAD JACOB ALFRED BRADLEY HERBERT FREDERICK EDWIN DON RICKY RANDALL BARRY BERNARD LEROY MARCUS THEODORE CLIFFORD MIGUEL JIM TOM CALVIN BILL LLOYD DEREK WARREN DARRELL JEROME FLOYD ALVIN TIM GORDON GREG JORGE DUSTIN PEDRO DERRICK ZACHARY HERMAN GLEN HECTOR RICARDO RICK BRENT RAMON GILBERT MARC REGINALD RUBEN NATHANIEL RAFAEL EDGAR MILTON RAUL BEN CHESTER DUANE FRANKLIN BRAD RON ROLAND ARNOLD HARVEY JARED ERIK DARRYL NEIL JAVIER FERNANDO CLINTON TED MATHEW TYRONE DARREN LANCE KURT ALLAN NELSON GUY CLAYTON HUGH MAX DWAYNE DWIGHT ARMANDO FELIX EVERETT IAN WALLACE KEN BOB ALFREDO ALBERTO DAVE IVAN BYRON ISAAC MORRIS CLIFTON WILLARD ROSS ANDY SALVADOR KIRK SERGIO SETH KENT TERRANCE EDUARDO TERRENCE ENRIQUE WADE STUART FREDRICK ARTURO ALEJANDRO NICK LUTHER WENDELL JEREMIAH JULIUS OTIS TREVOR OLIVER LUKE HOMER GERARD DOUG KENNY HUBERT LYLE MATT ALFONSO ORLANDO REX CARLTON ERNESTO NEAL PABLO LORENZO OMAR WILBUR GRANT HORACE RODERICK ABRAHAM WILLIS RICKEY ANDRES CESAR JOHNATHAN MALCOLM RUDOLPH DAMON KELVIN PRESTON ALTON ARCHIE MARCO WM PETE RANDOLPH GARRY GEOFFREY JONATHON FELIPE GERARDO ED DOMINIC DELBERT COLIN GUILLERMO EARNEST LUCAS BENNY SPENCER RODOLFO MYRON EDMUND GARRETT SALVATORE CEDRIC LOWELL GREGG SHERMAN WILSON SYLVESTER ROOSEVELT ISRAEL JERMAINE FORREST WILBERT LELAND SIMON CLARK IRVING BRYANT OWEN RUFUS WOODROW KRISTOPHER MACK LEVI MARCOS GUSTAVO JAKE LIONEL GILBERTO CLINT NICOLAS ISMAEL ORVILLE ERVIN DEWEY AL WILFRED JOSH HUGO IGNACIO CALEB TOMAS SHELDON ERICK STEWART DOYLE DARREL ROGELIO TERENCE SANTIAGO ALONZO ELIAS BERT ELBERT RAMIRO CONRAD NOAH GRADY PHIL CORNELIUS LAMAR ROLANDO CLAY PERCY DEXTER BRADFORD DARIN AMOS MOSES IRVIN SAUL ROMAN RANDAL TIMMY DARRIN WINSTON BRENDAN ABEL DOMINICK BOYD EMILIO ELIJAH DOMINGO EMMETT MARLON EMANUEL JERALD EDMOND EMIL DEWAYNE WILL OTTO TEDDY REYNALDO BRET JESS TRENT HUMBERTO EMMANUEL STEPHAN VICENTE LAMONT GARLAND MILES EFRAIN HEATH RODGER HARLEY ETHAN ELDON ROCKY PIERRE JUNIOR FREDDY ELI BRYCE ANTOINE STERLING CHASE GROVER ELTON CLEVELAND DYLAN CHUCK DAMIAN REUBEN STAN AUGUST LEONARDO JASPER RUSSEL ERWIN BENITO HANS MONTE BLAINE ERNIE CURT QUENTIN AGUSTIN MURRAY JAMAL ADOLFO HARRISON TYSON BURTON BRADY ELLIOTT WILFREDO BART JARROD VANCE DENIS DAMIEN JOAQUIN HARLAN DESMOND ELLIOT DARWIN GREGORIO BUDDY XAVIER KERMIT ROSCOE ESTEBAN ANTON SOLOMON SCOTTY NORBERT ELVIN WILLIAMS NOLAN ROD QUINTON HAL BRAIN ROB ELWOOD KENDRICK DARIUS MOISES FIDEL THADDEUS CLIFF MARCEL JACKSON RAPHAEL BRYON ARMAND ALVARO JEFFRY DANE JOESPH THURMAN NED RUSTY MONTY FABIAN REGGIE MASON GRAHAM ISAIAH VAUGHN GUS LOYD DIEGO ADOLPH NORRIS MILLARD ROCCO GONZALO DERICK RODRIGO WILEY RIGOBERTO ALPHONSO TY NOE VERN REED JEFFERSON ELVIS BERNARDO MAURICIO HIRAM DONOVAN BASIL RILEY NICKOLAS MAYNARD SCOT VINCE QUINCY EDDY SEBASTIAN FEDERICO ULYSSES HERIBERTO DONNELL COLE DAVIS GAVIN EMERY WARD ROMEO JAYSON DANTE CLEMENT COY MAXWELL JARVIS BRUNO ISSAC DUDLEY BROCK SANFORD CARMELO BARNEY NESTOR STEFAN DONNY ART LINWOOD BEAU WELDON GALEN ISIDRO TRUMAN DELMAR JOHNATHON SILAS FREDERIC DICK IRWIN MERLIN CHARLEY MARCELINO HARRIS CARLO TRENTON KURTIS HUNTER AURELIO WINFRED VITO COLLIN DENVER CARTER LEONEL EMORY PASQUALE MOHAMMAD MARIANO DANIAL LANDON DIRK BRANDEN ADAN BUFORD GERMAN WILMER EMERSON ZACHERY FLETCHER JACQUES ERROL DALTON MONROE JOSUE EDWARDO BOOKER WILFORD SONNY SHELTON CARSON THERON RAYMUNDO DAREN HOUSTON ROBBY LINCOLN GENARO BENNETT OCTAVIO CORNELL HUNG ARRON ANTONY HERSCHEL GIOVANNI GARTH CYRUS CYRIL RONNY LON FREEMAN DUNCAN KENNITH CARMINE ERICH CHADWICK WILBURN RUSS REID MYLES ANDERSON MORTON JONAS FOREST MITCHEL MERVIN ZANE RICH JAMEL LAZARO ALPHONSE RANDELL MAJOR JARRETT BROOKS ABDUL LUCIANO SEYMOUR EUGENIO MOHAMMED VALENTIN CHANCE ARNULFO LUCIEN FERDINAND THAD EZRA ALDO RUBIN ROYAL MITCH EARLE ABE WYATT MARQUIS LANNY KAREEM JAMAR BORIS ISIAH EMILE ELMO ARON LEOPOLDO EVERETTE JOSEF ELOY RODRICK REINALDO LUCIO JERROD WESTON HERSHEL BARTON PARKER LEMUEL BURT JULES GIL ELISEO AHMAD NIGEL EFREN ANTWAN ALDEN MARGARITO COLEMAN DINO OSVALDO LES DEANDRE NORMAND KIETH TREY NORBERTO NAPOLEON JEROLD FRITZ ROSENDO MILFORD CHRISTOPER ALFONZO LYMAN JOSIAH BRANT WILTON RICO JAMAAL DEWITT BRENTON OLIN FOSTER FAUSTINO CLAUDIO JUDSON GINO EDGARDO ALEC TANNER JARRED DONN TAD PRINCE PORFIRIO ODIS LENARD CHAUNCEY TOD MEL MARCELO KORY AUGUSTUS KEVEN HILARIO BUD SAL ORVAL MAURO ZACHARIAH OLEN ANIBAL MILO JED DILLON AMADO NEWTON LENNY RICHIE HORACIO BRICE MOHAMED DELMER DARIO REYES MAC JONAH JERROLD ROBT HANK RUPERT ROLLAND KENTON DAMION ANTONE WALDO FREDRIC BRADLY KIP BURL WALKER TYREE JEFFEREY AHMED WILLY STANFORD OREN NOBLE MOSHE MIKEL ENOCH BRENDON QUINTIN JAMISON FLORENCIO DARRICK TOBIAS HASSAN GIUSEPPE DEMARCUS CLETUS TYRELL LYNDON KEENAN WERNER GERALDO COLUMBUS CHET BERTRAM MARKUS HUEY HILTON DWAIN DONTE TYRON OMER ISAIAS HIPOLITO FERMIN ADALBERTO BO BARRETT TEODORO MCKINLEY MAXIMO GARFIELD RALEIGH LAWERENCE ABRAM RASHAD KING EMMITT DARON SAMUAL MIQUEL EUSEBIO DOMENIC DARRON BUSTER WILBER RENATO JC HOYT HAYWOOD EZEKIEL CHAS FLORENTINO ELROY CLEMENTE ARDEN NEVILLE EDISON DESHAWN NATHANIAL JORDON DANILO CLAUD SHERWOOD RAYMON RAYFORD CRISTOBAL AMBROSE TITUS HYMAN FELTON EZEQUIEL ERASMO STANTON LONNY LEN IKE MILAN LINO JAROD HERB ANDREAS WALTON RHETT PALMER DOUGLASS CORDELL OSWALDO ELLSWORTH VIRGILIO TONEY NATHANAEL DEL BENEDICT MOSE JOHNSON ISREAL GARRET FAUSTO ASA ARLEN ZACK WARNER MODESTO FRANCESCO MANUAL GAYLORD GASTON FILIBERTO DEANGELO MICHALE GRANVILLE WES MALIK ZACKARY TUAN ELDRIDGE CRISTOPHER CORTEZ ANTIONE MALCOM LONG KOREY JOSPEH COLTON WAYLON VON HOSEA SHAD SANTO RUDOLF ROLF REY RENALDO MARCELLUS LUCIUS KRISTOFER BOYCE BENTON HAYDEN HARLAND ARNOLDO RUEBEN LEANDRO KRAIG JERRELL JEROMY HOBERT CEDRICK ARLIE WINFORD WALLY LUIGI KENETH JACINTO GRAIG FRANKLYN EDMUNDO SID PORTER LEIF JERAMY BUCK WILLIAN VINCENZO SHON LYNWOOD JERE HAI ELDEN DORSEY DARELL BRODERICK ALONSO".split(" ") + +names.sort() + +var sum = 0 + +for (var i : var name in names) { + var score = 0 + for (var c in name) { + score += c.code() - '@'.code() + } + sum += score * (i + 1) +} + +return sum; \ No newline at end of file diff --git a/src/test/resources/ai/euler/pe023.leek b/src/test/resources/ai/euler/pe023.leek new file mode 100644 index 00000000..c98abaa0 --- /dev/null +++ b/src/test/resources/ai/euler/pe023.leek @@ -0,0 +1,39 @@ +function isAbundant(n) { + var sum = 0; + var half = n \ 2; + for (var i = 1; i <= half; ++i) + if (n % i == 0) { + sum += i + } + return sum > n +} + +let N = 28123 + +var numbers = [].fill(0, N) +var abundant = [].fill(false, N) +var canBeWritten = [].fill(false, N) + +for (var i = 0; i < N; ++i) { + numbers[i] = i + 1 + abundant[i] = isAbundant(i + 1) + canBeWritten[i] = false +} + +for (var i = 0; i < N; ++i) { + if (abundant[i]) { + for (var j = 0; j < N; ++j) { + if (abundant[j]) { + var sum = numbers[i] + numbers[j]; + if (sum <= N) canBeWritten[sum - 1] = true; + } + } + } +} + +var sum = 0; +for (var i = 0; i < N; ++i) { + if (!canBeWritten[i]) sum += (i + 1); +} + +sum \ No newline at end of file diff --git a/src/test/resources/ai/euler/pe024.leek b/src/test/resources/ai/euler/pe024.leek new file mode 100644 index 00000000..7bd42ac2 --- /dev/null +++ b/src/test/resources/ai/euler/pe024.leek @@ -0,0 +1,28 @@ +var p = 1 + +for (var a = 0; a <= 9; ++a) +for (var b = 0; b <= 9; ++b) +if (b != a) +for (var c = 0; c <= 9; ++c) +if (c != b && c != a) +for (var d = 0; d <= 9; ++d) +if (d != c && d != b && d != a) +for (var e = 0; e <= 9; ++e) +if (e != d && e != c && e != b && e != a) +for (var f = 0; f <= 9; ++f) +if (f != e && f != d && f != c && f != b && f != a) +for (var g = 0; g <= 9; ++g) +if (g != f && g != e && g != d && g != c && g != b && g != a) +for (var h = 0; h <= 9; ++h) +if (h != g && h != f && h != e && h != d && h != c && h != b && h != a) +for (var i = 0; i <= 9; ++i) +if (i != h && i != g && i != f && i != e && i != d && i != c && i != b && i != a) +for (var j = 0; j <= 9; ++j) +if (j != i && j != h && j != g && j != f && j != e && j != d && j != c && j != b && j != a) { + + var n = a*1000000000L + b*100000000L + c*10000000L + d*1000000 + e*100000 + f*10000 + g*1000 + h*100 + i * 10 + j + + if (p++ == 1000000) { + return n + } +} \ No newline at end of file diff --git a/src/test/resources/ai/euler/pe025.leek b/src/test/resources/ai/euler/pe025.leek new file mode 100644 index 00000000..987111f9 --- /dev/null +++ b/src/test/resources/ai/euler/pe025.leek @@ -0,0 +1,15 @@ +var n1 = 1m +var n2 = 1m + +var t = 2 + +while |n1.string()| < 1000 { + + var tmp = n1 + n1 = n1 + n2 + n2 = tmp + + t++ +} + +t \ No newline at end of file diff --git a/src/test/resources/ai/euler/pe027.leek b/src/test/resources/ai/euler/pe027.leek new file mode 100644 index 00000000..350a7fa2 --- /dev/null +++ b/src/test/resources/ai/euler/pe027.leek @@ -0,0 +1,33 @@ +function numPrimes(a, b) { + var n = 0 + // TODO while (n*n + a*n + b).isPrime() not working + while ((n*n + a*n + b).isPrime()) { n++ } + return n +} + +// print(numPrimes(-79, 1601)) + +var max = 0; +var maxA = 0, maxB = 0; + +for (var a = -999; a < 1000; ++a) { + for (var b = -999; b < 1000; ++b) { + + if (b.isPrime()) { // b doit etre premier ! + + var num = numPrimes(a, b); + if (num > max) { + max = num + maxA = a + maxB = b + } + } + } +} +/* +print("Nombre de premiers : " + max) +print("a : " + maxA) +print("b : " + maxB) +print("ab : " + maxA * maxB) +*/ +return maxA * maxB diff --git a/src/test/resources/ai/euler/pe028.leek b/src/test/resources/ai/euler/pe028.leek new file mode 100644 index 00000000..9ac3cdfd --- /dev/null +++ b/src/test/resources/ai/euler/pe028.leek @@ -0,0 +1,15 @@ + + +var size = 1001 +var n = 1m +var steps = size \ 2 + +for i in [1..steps] { + var x = 2*i + 1 + n += x*x + n += x*x - 6*i + n += x*x - 4*i + n += x*x - 2*i +} + +n \ No newline at end of file diff --git a/src/test/resources/ai/euler/pe062.leek b/src/test/resources/ai/euler/pe062.leek new file mode 100644 index 00000000..c72f80bb --- /dev/null +++ b/src/test/resources/ai/euler/pe062.leek @@ -0,0 +1,20 @@ +var singles = [] +var groups = ['s' : 12] groups.erase('s') +var first = [:] +var i = 405l + +while i++ { + + var n = '' + i.pow(3) + var ns = n.sort() + + if ns in groups { + var c = groups[ns] = groups[ns] + 1 + if (c == 5) { + return String.number(first[ns]) + } + } else { + groups.insert(ns, 1) + first.insert(ns, n) + } +} diff --git a/src/test/resources/ai/euler/pe063.leek b/src/test/resources/ai/euler/pe063.leek new file mode 100644 index 00000000..795537c5 --- /dev/null +++ b/src/test/resources/ai/euler/pe063.leek @@ -0,0 +1,10 @@ +var c = 0 +for (var power = 1m; power < 25m; power++) { + for (var n = 1m; n < 10m; n++) { + var s = '' + (n ** power) + if power == s.size() { + c++ + } + } +} +c diff --git a/src/test/resources/ai/euler/pe064.leek b/src/test/resources/ai/euler/pe064.leek new file mode 100644 index 00000000..f120c2e5 --- /dev/null +++ b/src/test/resources/ai/euler/pe064.leek @@ -0,0 +1,11 @@ +let period = n => { + var s = n.sqrt(), a0 = s.int() + if (n == a0 * a0) return 0 + var a = a0 var b = 1 var l = 0 // TODO with commas + while ++l { + b = (n - a * a) \ b + a = b * ((s + a) \ b) - a + if (a == a0 and b == 1) return l + } +} +[2..10000].filter(i => period(i) % 2).size() \ No newline at end of file diff --git a/src/test/resources/ai/euler/pe066.leek b/src/test/resources/ai/euler/pe066.leek new file mode 100644 index 00000000..18ac3703 --- /dev/null +++ b/src/test/resources/ai/euler/pe066.leek @@ -0,0 +1,23 @@ +function solve(D) { + var x = 1l + while x++ { + var s = ((x * x - 1) / D).sqrt() + if s.long() == s { + print((x * x - 1) / D) + print(s) + print(s.long()) + return x + } + } +} + +solve(61) +/* +var max = 0 +var maxD = 0 +for D in [1..1000] { + if D.sqrt().isInteger() continue + var x = solve(D) + x > max ? { max = x maxD = D } : 0 +} +maxD \ No newline at end of file diff --git a/src/test/resources/ai/euler/pe206.leek b/src/test/resources/ai/euler/pe206.leek new file mode 100644 index 00000000..0566af03 --- /dev/null +++ b/src/test/resources/ai/euler/pe206.leek @@ -0,0 +1,24 @@ +var res = '' +var search = function(start, base, level) { + if (level == 5) { + var b = base + (10m ** (20 - 2 * level)) + for (var j = Number.sqrt(start); j < Number.sqrt(b); ++j) { + var s = '' + (j ** 2) + for var k = 0; k < 9; { + if s[k * 2] != ('' + (++k)) continue 2 + } + res += ('' + j) + } + } else { + for var i = 0m; i < 10m; ++i { + var p = i * (10m ** (19 - 2 * level)) + var a = start + p + var b = base + (level + 1) * (10m ** (18 - 2 * level)) + p + search(a, b, level + 1) + } + } +} + +search(1020304050607080900m, 1000000000000000000m, 1) + +res.number() diff --git a/src/test/resources/ai/french.leek b/src/test/resources/ai/french.leek new file mode 100644 index 00000000..ef2a01ca --- /dev/null +++ b/src/test/resources/ai/french.leek @@ -0,0 +1,6 @@ +function _(n) { + var T = '-', V = '', x = split('s un deux trois quatre cinq six sept huit neuf dix onze douze treize quatorze quinze seize dix vingt trente quarante cinquante soixante quatre-vingt cent' ' '), b = n % 100, d = 17 + b / 10, u = n % 10, c = n / 100 | 0, e = u - 1 ? T : '-et-', L = log10(n) / 3 | 0, H = 1000 ** L, v = n / H | 0, i = [T ' ' x[d] ? 0 : d-- | (u += 10)][L > 1]; + return L < 1 ? c ? (c > 1 ? _(c) + T : V) + x[27] + x[c * !b < 2] + (b ? T : V) + _(b) : n < 17 ? x[n + 1] : n < 20 ? x[11] + T + x[u + 1] : x[d] + (u ? e + _(u) : x[d != 25]) : (L * v > 1 ? _(v) + i : V) + 'mill' + [n %= H 'e' 'ion' 'iard'][L] + x[v < 2 | L < 2] + (n ? i : V) + _(n); +} + +return _(123456789); \ No newline at end of file diff --git a/src/test/resources/ai/include_multiple.leek b/src/test/resources/ai/include_multiple.leek new file mode 100644 index 00000000..aaaa73d6 --- /dev/null +++ b/src/test/resources/ai/include_multiple.leek @@ -0,0 +1,2 @@ + +include("array_keys.leek"); \ No newline at end of file diff --git a/src/test/resources/ai/include_sub.leek b/src/test/resources/ai/include_sub.leek new file mode 100644 index 00000000..6e944acd --- /dev/null +++ b/src/test/resources/ai/include_sub.leek @@ -0,0 +1 @@ +include('subfolder/sub.leek'); \ No newline at end of file diff --git a/src/test/resources/ai/library.leek b/src/test/resources/ai/library.leek new file mode 100644 index 00000000..513fa824 --- /dev/null +++ b/src/test/resources/ai/library.leek @@ -0,0 +1,5 @@ +function arrayKeys(array) { + var keys = []; + for (var k : var _ in array) push(keys, k); + return keys; +} \ No newline at end of file diff --git a/src/test/resources/ai/multiple_includes.leek b/src/test/resources/ai/multiple_includes.leek new file mode 100644 index 00000000..2e975a1c --- /dev/null +++ b/src/test/resources/ai/multiple_includes.leek @@ -0,0 +1,3 @@ +include("bonjour.leek"); +include("bonjour.leek"); +include("bonjour.leek"); \ No newline at end of file diff --git a/src/test/resources/ai/subfolder/include_parent.leek b/src/test/resources/ai/subfolder/include_parent.leek new file mode 100644 index 00000000..1684b3f4 --- /dev/null +++ b/src/test/resources/ai/subfolder/include_parent.leek @@ -0,0 +1 @@ +include('../french.leek'); \ No newline at end of file diff --git a/src/test/resources/ai/subfolder/sub.leek b/src/test/resources/ai/subfolder/sub.leek new file mode 100644 index 00000000..fcedbf97 --- /dev/null +++ b/src/test/resources/ai/subfolder/sub.leek @@ -0,0 +1 @@ +return 'sub'; \ No newline at end of file