diff --git a/.gitignore b/.gitignore index 33355e2..a4c1678 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .nb-gradle build .gradle +run diff --git a/MineTweaker3-API/build.gradle b/MineTweaker3-API/build.gradle new file mode 100644 index 0000000..9d5e580 --- /dev/null +++ b/MineTweaker3-API/build.gradle @@ -0,0 +1,14 @@ +apply plugin: 'java' + +sourceCompatibility = '1.6' +targetCompatibility = '1.6' + +task run << { + println "I'm a library!" +} + +dependencies { + dependencies { + compile project(':ZenScript') + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/GlobalFunctions.java b/MineTweaker3-API/src/main/java/minetweaker/GlobalFunctions.java new file mode 100644 index 0000000..1de8e6b --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/GlobalFunctions.java @@ -0,0 +1,19 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker; + +/** + * + * @author Stan + */ +public class GlobalFunctions { + private GlobalFunctions() {} + + public static void print(String message) { + MineTweakerAPI.logger.logInfo(message); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/GlobalRegistry.java b/MineTweaker3-API/src/main/java/minetweaker/GlobalRegistry.java new file mode 100644 index 0000000..d63bc9d --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/GlobalRegistry.java @@ -0,0 +1,211 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import minetweaker.minecraft.item.IIngredient; +import stanhebben.zenscript.IZenErrorLogger; +import stanhebben.zenscript.TypeExpansion; +import stanhebben.zenscript.annotations.ZenExpansion; +import stanhebben.zenscript.compiler.TypeRegistry; +import stanhebben.zenscript.parser.Token; +import stanhebben.zenscript.symbols.IZenCompileEnvironment; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.symbols.SymbolJavaStaticField; +import stanhebben.zenscript.symbols.SymbolJavaStaticMethod; +import stanhebben.zenscript.symbols.SymbolPackage; +import stanhebben.zenscript.type.ZenTypeNative; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +public class GlobalRegistry { + private static final Map globals = new HashMap(); + private static final List removers = new ArrayList(); + private static final List bracketHandlers = new ArrayList(); + private static final TypeRegistry types = new TypeRegistry(); + private static final SymbolPackage root = new SymbolPackage(); + private static final IZenErrorLogger errors = new MyErrorLogger(); + private static final IZenCompileEnvironment environment = new MyCompileEnvironment(); + private static final Map expansions = new HashMap(); + + static { + register("print", getStaticFunction(GlobalFunctions.class, "print", String.class)); + register("max", getStaticFunction(Math.class, "max", int.class, int.class)); + register("min", getStaticFunction(Math.class, "min", int.class, int.class)); + + register("logger", getStaticField(MineTweakerAPI.class, "logger")); + register("minetweaker", getStaticField(MineTweakerAPI.class, "tweaker")); + register("recipes", getStaticField(MineTweakerAPI.class, "recipes")); + register("furnace", getStaticField(MineTweakerAPI.class, "furnace")); + register("oreDict", getStaticField(MineTweakerAPI.class, "oreDict")); + + /*registerExpansion(ExpandAnyArray.class); + registerExpansion(ExpandAnyDict.class); + registerExpansion(ExpandBool.class); + registerExpansion(ExpandByte.class); + registerExpansion(ExpandDouble.class); + registerExpansion(ExpandFloat.class); + registerExpansion(ExpandInt.class); + registerExpansion(ExpandLong.class); + registerExpansion(ExpandShort.class); + registerExpansion(ExpandString.class);*/ + + /*registerNativeClass(IData.class); + + registerNativeClass(IIngredient.class); + registerNativeClass(IItemCondition.class); + registerNativeClass(IItemDefinition.class); + registerNativeClass(IItemStack.class); + registerNativeClass(IItemTransformer.class); + registerNativeClass(Condition.class); + registerNativeClass(Transform.class); + + registerNativeClass(ILiquidDefinition.class); + registerNativeClass(ILiquidStack.class); + + registerNativeClass(IOreDict.class); + registerNativeClass(IOreDictEntry.class); + + registerNativeClass(ICraftingInventory.class); + registerNativeClass(IFurnaceManager.class); + registerNativeClass(IRecipeFunction.class);*/ + } + + private GlobalRegistry() {} + + public static void register(String name, IZenSymbol symbol) { + if (globals.containsKey(name)) { + throw new IllegalArgumentException("symbol already exists: " + name); + } + + globals.put(name, symbol); + } + + public static void registerExpansion(Class cls) { + for (Annotation annotation : cls.getAnnotationsByType(ZenExpansion.class)) { + ZenExpansion eAnnotation = (ZenExpansion) annotation; + if (!expansions.containsKey(eAnnotation.value())) { + expansions.put(eAnnotation.value(), new TypeExpansion()); + } + expansions.get(eAnnotation.value()).expand(cls, types); + } + } + + public static void registerRemover(IRecipeRemover remover) { + removers.add(remover); + } + + public static void registerBracketHandler(IBracketHandler handler) { + bracketHandlers.add(handler); + } + + public static void registerNativeClass(Class cls) { + ZenTypeNative type = new ZenTypeNative(cls); + type.complete(types); + + root.put(type.getName(), root, errors); + } + + public static TypeRegistry getTypeRegistry() { + return types; + } + + public static void remove(IIngredient ingredient) { + for (IRecipeRemover remover : removers) { + remover.remove(ingredient); + } + } + + public static IZenSymbol resolveBracket(List tokens) { + for (IBracketHandler handler : bracketHandlers) { + IZenSymbol symbol = handler.resolve(tokens); + if (symbol != null) { + return symbol; + } + } + + return null; + } + + public static IZenSymbol getStaticFunction(Class cls, String name, Class... arguments) { + JavaMethod method = JavaMethod.get(types, cls, name, arguments); + if (method == null) return null; + return new SymbolJavaStaticMethod(method); + } + + public static IZenSymbol getStaticField(Class cls, String name) { + try { + Field field = cls.getDeclaredField(name); + return new SymbolJavaStaticField(cls, field, types); + } catch (NoSuchFieldException ex) { + Logger.getLogger(GlobalRegistry.class.getName()).log(Level.SEVERE, null, ex); + return null; + } catch (SecurityException ex) { + Logger.getLogger(GlobalRegistry.class.getName()).log(Level.SEVERE, null, ex); + return null; + } + } + + private static class MyErrorLogger implements IZenErrorLogger { + @Override + public void error(ZenPosition position, String message) { + MineTweakerAPI.logger.logError(position.toString() + ": " + message); + } + + @Override + public void warning(ZenPosition position, String message) { + MineTweakerAPI.logger.logWarning(position.toString() + ": " + message); + } + } + + private static class MyCompileEnvironment implements IZenCompileEnvironment { + @Override + public IZenErrorLogger getErrorLogger() { + return errors; + } + + @Override + public IZenSymbol getGlobal(String name) { + if (globals.containsKey(name)) { + return globals.get(name); + } else { + return root.get(name); + } + } + + @Override + public IZenSymbol getDollar(String name) { + return null; + } + + @Override + public IZenSymbol getBracketed(List tokens) { + return resolveBracket(tokens); + } + + @Override + public TypeRegistry getTypeRegistry() { + return types; + } + + @Override + public TypeExpansion getExpansion(String type) { + return expansions.get(type); + } + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/IBracketHandler.java b/MineTweaker3-API/src/main/java/minetweaker/IBracketHandler.java new file mode 100644 index 0000000..24d58f2 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/IBracketHandler.java @@ -0,0 +1,19 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker; + +import java.util.List; +import stanhebben.zenscript.parser.Token; +import stanhebben.zenscript.symbols.IZenSymbol; + +/** + * + * @author Stan + */ +public interface IBracketHandler { + public IZenSymbol resolve(List tokens); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/ILogger.java b/MineTweaker3-API/src/main/java/minetweaker/ILogger.java new file mode 100644 index 0000000..feb2688 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/ILogger.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker; + +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * + * @author Stan + */ +public interface ILogger { + @ZenMethod + public void logInfo(String message); + + @ZenMethod + public void logWarning(String message); + + @ZenMethod + public void logError(String message); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/IMineTweaker.java b/MineTweaker3-API/src/main/java/minetweaker/IMineTweaker.java new file mode 100644 index 0000000..a1ba5a5 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/IMineTweaker.java @@ -0,0 +1,44 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker; + +import java.util.List; +import minetweaker.minecraft.item.IIngredient; +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * + * @author Stan + */ +@ZenClass("minetweaker.MineTweaker") +public interface IMineTweaker { + /** + * Executes a specified MineTweaker action. Will print a log message and + * adds the action to the undo list. + * + * @param action action to execute + */ + public void apply(IUndoableAction action); + + /** + * Removes a specific item from all known recipes handlers. + * + * @param items items to remove + */ + @ZenMethod + public void remove(IIngredient items); + + /** + * Rolls back all actions performed by MineTweaker. Returns the list of + * actions that could not be rolled back (the "stuck" ones that are not + * undoable). + * + * @return stuck action list + */ + public List rollback(); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/IRecipeRemover.java b/MineTweaker3-API/src/main/java/minetweaker/IRecipeRemover.java new file mode 100644 index 0000000..026998d --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/IRecipeRemover.java @@ -0,0 +1,17 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker; + +import minetweaker.minecraft.item.IIngredient; + +/** + * + * @author Stan + */ +public interface IRecipeRemover { + public void remove(IIngredient ingredient); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/IUndoableAction.java b/MineTweaker3-API/src/main/java/minetweaker/IUndoableAction.java new file mode 100644 index 0000000..9200f19 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/IUndoableAction.java @@ -0,0 +1,63 @@ +package minetweaker; + +/** + * Defines a (possibly) undoable action. + * + * Every modification made by a command must be registered to the Tweaker with + * the Tweaker.apply method. This enables MineTweaker to keep track of actions + * that have been performed, and enables them to be rolled back later on. + * + * Actions exist in two flavors: undoable and permanent. If an action returns + * canUndo(), the action is undoable, otherwise it is permanent. Permanent + * actions cannot be used in server scripts. + * + * @author Stan Hebben + */ +public interface IUndoableAction { + /** + * Executes what the action is supposed to do. This method can be called + * again if undo() has been called in between. + */ + public void apply(); + + /** + * Checks if this action can be undone. If this method returns true, it + * must implement undo() properly. If this method returns false, the action + * is considered permanent, undo() will never be called, and the action + * cannot be executed as part of a server script. Additionally, the + * minetweaker.clear() function will not work and minetweaker.canClear + * will return false. + * + * @return undoable status + */ + public boolean canUndo(); + + /** + * Undoes the action. Will only be called after apply() has been executed. + * After an undo, apply may be called again. They could be done multiple + * times in certain scenarios. + */ + public void undo(); + + /** + * Describes, in a single human-readable sentence, what this specific action + * is doing. Used in logging messages, lists, ... + * + * Try to be as descriptive as possible without being too verbose. + * + * Examples: + * - Adding Peach planks to the woodPlanks ore dictionary entry + * - Removing a recipe for Iron Ore + * + * @return the description of this action + */ + public String describe(); + + /** + * Describes what this action does if it is undone. Similar to the describe() + * method. No implementation is needed if the action cannot be undone. + * + * @return the description of this action, when undone + */ + public String describeUndo(); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/MineTweakerAPI.java b/MineTweaker3-API/src/main/java/minetweaker/MineTweakerAPI.java new file mode 100644 index 0000000..9b4dcc9 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/MineTweakerAPI.java @@ -0,0 +1,53 @@ +package minetweaker; + +import minetweaker.minecraft.recipes.IRecipeManager; +import minetweaker.minecraft.oredict.IOreDict; +import minetweaker.minecraft.recipes.IFurnaceManager; + +/** + * Provides access to the MineTweaker API. + * + * An implementing platform needs to do the following: + * - Set a logger + * - Set the ore dictionary + * - Set the recipe manager + * - Set the furnace manager + * + * - Register additional global symbols to the GlobalRegistry (recipes, + * minetweaker, oreDict, logger, as well as the official set of functions) + * - Register native classes using the GlobalRegistry + * - Register bracket handlers to resolve block/item/... references using the + * bracket syntax + * + * @author Stan Hebben + */ +public class MineTweakerAPI { + private MineTweakerAPI() {} + + /** + * The Tweaker is where you apply undoable actions. Any kind of action that + * reloads with the scripts should always be submitted to the tweaker. + */ + public static final IMineTweaker tweaker = new Tweaker(); + + /** + * The logger can be used to write logging messages to the client. Error and + * warning messages should be relayed to admins for further handling. + */ + public static ILogger logger = null; + + /** + * Access point to the ore dictionary. + */ + public static IOreDict oreDict = null; + + /** + * Access point to the recipe manager. + */ + public static IRecipeManager recipes = null; + + /** + * Access point to the furnace manager. + */ + public static IFurnaceManager furnace = null; +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/OneWayAction.java b/MineTweaker3-API/src/main/java/minetweaker/OneWayAction.java new file mode 100644 index 0000000..17ba657 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/OneWayAction.java @@ -0,0 +1,26 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker; + +/** + * + * @author Stanneke + */ +public abstract class OneWayAction implements IUndoableAction { + @Override + public boolean canUndo() { + return false; + } + + @Override + public void undo() {} + + @Override + public String describeUndo() { + return "impossibru!"; + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/Tweaker.java b/MineTweaker3-API/src/main/java/minetweaker/Tweaker.java new file mode 100644 index 0000000..dca257d --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/Tweaker.java @@ -0,0 +1,50 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import minetweaker.minecraft.item.IIngredient; + +/** + * + * + * @author Stan Hebben + */ +public class Tweaker implements IMineTweaker { + List actions = new ArrayList(); + Set wereStuck = new HashSet(); + + @Override + public void apply(IUndoableAction action) { + MineTweakerAPI.logger.logInfo(action.describe()); + action.apply(); + } + + @Override + public void remove(IIngredient items) { + GlobalRegistry.remove(items); + } + + @Override + public List rollback() { + List stuck = new ArrayList(); + for (int i = actions.size() - 1; i >= 0; i--) { + IUndoableAction action = actions.get(i); + if (action.canUndo()) { + MineTweakerAPI.logger.logInfo(action.describeUndo()); + action.undo(); + } else { + stuck.add(0, action); + wereStuck.add(action); + } + } + return stuck; + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/annotations/ModOnly.java b/MineTweaker3-API/src/main/java/minetweaker/annotations/ModOnly.java new file mode 100644 index 0000000..f2b8112 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/annotations/ModOnly.java @@ -0,0 +1,22 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Stan + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface ModOnly { + public String value(); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandAnyArray.java b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandAnyArray.java new file mode 100644 index 0000000..ce3644f --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandAnyArray.java @@ -0,0 +1,25 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.expand; + +import java.util.Arrays; +import minetweaker.minecraft.data.DataList; +import minetweaker.minecraft.data.IData; +import stanhebben.zenscript.annotations.ZenCaster; +import stanhebben.zenscript.annotations.ZenExpansion; + +/** + * + * @author Stanneke + */ +@ZenExpansion("any[]") +public class ExpandAnyArray { + @ZenCaster + public IData asData(IData[] values) { + return new DataList(Arrays.asList(values), true); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandAnyDict.java b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandAnyDict.java new file mode 100644 index 0000000..99ed0d5 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandAnyDict.java @@ -0,0 +1,25 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.expand; + +import java.util.Map; +import minetweaker.minecraft.data.DataMap; +import minetweaker.minecraft.data.IData; +import stanhebben.zenscript.annotations.ZenCaster; +import stanhebben.zenscript.annotations.ZenExpansion; + +/** + * + * @author Stanneke + */ +@ZenExpansion("any[any]") +public class ExpandAnyDict { + @ZenCaster + public IData asData(Map values) { + return new DataMap(values, true); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandBool.java b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandBool.java new file mode 100644 index 0000000..57c880b --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandBool.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.expand; + +import minetweaker.minecraft.data.DataBool; +import minetweaker.minecraft.data.IData; +import stanhebben.zenscript.annotations.ZenCaster; +import stanhebben.zenscript.annotations.ZenExpansion; + +/** + * + * @author Stanneke + */ +@ZenExpansion("bool") +public class ExpandBool { + @ZenCaster + public IData asData(boolean value) { + return new DataBool(value); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandByte.java b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandByte.java new file mode 100644 index 0000000..0dc7223 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandByte.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.expand; + +import minetweaker.minecraft.data.DataByte; +import minetweaker.minecraft.data.IData; +import stanhebben.zenscript.annotations.ZenCaster; +import stanhebben.zenscript.annotations.ZenExpansion; + +/** + * + * @author Stanneke + */ +@ZenExpansion("byte") +public class ExpandByte { + @ZenCaster + public IData asData(byte value) { + return new DataByte(value); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandDouble.java b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandDouble.java new file mode 100644 index 0000000..4a4708d --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandDouble.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.expand; + +import minetweaker.minecraft.data.DataDouble; +import minetweaker.minecraft.data.IData; +import stanhebben.zenscript.annotations.ZenCaster; +import stanhebben.zenscript.annotations.ZenExpansion; + +/** + * + * @author Stanneke + */ +@ZenExpansion("double") +public class ExpandDouble { + @ZenCaster + public IData asData(double value) { + return new DataDouble(value); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandFloat.java b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandFloat.java new file mode 100644 index 0000000..8490647 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandFloat.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.expand; + +import minetweaker.minecraft.data.DataFloat; +import minetweaker.minecraft.data.IData; +import stanhebben.zenscript.annotations.ZenCaster; +import stanhebben.zenscript.annotations.ZenExpansion; + +/** + * + * @author Stanneke + */ +@ZenExpansion("float") +public class ExpandFloat { + @ZenCaster + public IData asData(float value) { + return new DataFloat(value); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandInt.java b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandInt.java new file mode 100644 index 0000000..7712138 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandInt.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.expand; + +import minetweaker.minecraft.data.DataInt; +import minetweaker.minecraft.data.IData; +import stanhebben.zenscript.annotations.ZenCaster; +import stanhebben.zenscript.annotations.ZenExpansion; + +/** + * + * @author Stanneke + */ +@ZenExpansion("int") +public class ExpandInt { + @ZenCaster + public IData toData(int value) { + return new DataInt(value); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandLong.java b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandLong.java new file mode 100644 index 0000000..eff667d --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandLong.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.expand; + +import minetweaker.minecraft.data.DataLong; +import minetweaker.minecraft.data.IData; +import stanhebben.zenscript.annotations.ZenCaster; +import stanhebben.zenscript.annotations.ZenExpansion; + +/** + * + * @author Stanneke + */ +@ZenExpansion("long") +public class ExpandLong { + @ZenCaster + public IData asData(long value) { + return new DataLong(value); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandShort.java b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandShort.java new file mode 100644 index 0000000..3be416f --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandShort.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.expand; + +import minetweaker.minecraft.data.DataShort; +import minetweaker.minecraft.data.IData; +import stanhebben.zenscript.annotations.ZenCaster; +import stanhebben.zenscript.annotations.ZenExpansion; + +/** + * + * @author Stanneke + */ +@ZenExpansion("short") +public class ExpandShort { + @ZenCaster + public IData asNBT(short value) { + return new DataShort(value); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandString.java b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandString.java new file mode 100644 index 0000000..d3a05cc --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/expand/ExpandString.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.expand; + +import minetweaker.minecraft.data.DataString; +import minetweaker.minecraft.data.IData; +import stanhebben.zenscript.annotations.ZenCaster; +import stanhebben.zenscript.annotations.ZenExpansion; + +/** + * + * @author Stanneke + */ +@ZenExpansion("string") +public class ExpandString { + @ZenCaster + public IData asData(String value) { + return new DataString(value); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataBool.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataBool.java new file mode 100644 index 0000000..25fb5f4 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataBool.java @@ -0,0 +1,187 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.data; + +import java.util.List; +import java.util.Map; + +/** + * + * @author Stan + */ +public class DataBool implements IData { + private final boolean value; + + public DataBool(boolean value) { + this.value = value; + } + + @Override + public boolean asBool() { + return value; + } + + @Override + public byte asByte() { + return (byte) (value ? 1 : 0); + } + + @Override + public short asShort() { + return (short) (value ? 1 : 0); + } + + @Override + public int asInt() { + return value ? 1 : 0; + } + + @Override + public long asLong() { + return value ? 1 : 0; + } + + @Override + public float asFloat() { + return value ? 1 : 0; + } + + @Override + public double asDouble() { + return value ? 1 : 0; + } + + @Override + public String asString() { + return Boolean.toString(value); + } + + @Override + public List asList() { + throw new UnsupportedOperationException("Cannot cast a bool to a list"); + } + + @Override + public Map asMap() { + throw new UnsupportedOperationException("Cannot cast a bool to a map"); + } + + @Override + public byte[] asByteArray() { + throw new RuntimeException("cannot cast a bool to a byte array"); + } + + @Override + public int[] asIntArray() { + throw new RuntimeException("cannot cast a bool to an int array"); + } + + @Override + public IData getAt(int i) { + throw new RuntimeException("a bool is not indexable"); + } + + @Override + public void setAt(int i, IData value) { + throw new RuntimeException("a bool is not indexable"); + } + + @Override + public IData memberGet(String name) { + throw new RuntimeException("a bool is not indexable"); + } + + @Override + public void memberSet(String name, IData data) { + throw new RuntimeException("a bool is not indexable"); + } + + @Override + public int length() { + return 0; + } + + @Override + public boolean contains(IData data) { + return data.asBool() == value; + } + + @Override + public boolean equals(IData other) { + return value == other.asBool(); + } + + @Override + public int compareTo(IData other) { + throw new UnsupportedOperationException("Cannot compare bool values"); + } + + @Override + public IData immutable() { + return this; + } + + @Override + public IData update(IData data) { + return data; + } + + @Override + public T convert(IDataConverter converter) { + return converter.fromBool(value); + } + + @Override + public IData add(IData other) { + throw new UnsupportedOperationException("Cannot add bool values"); + } + + @Override + public IData sub(IData other) { + throw new UnsupportedOperationException("Cannot subtract bool values"); + } + + @Override + public IData mul(IData other) { + throw new UnsupportedOperationException("Cannot multiply bool values"); + } + + @Override + public IData div(IData other) { + throw new UnsupportedOperationException("Cannot divide bool values"); + } + + @Override + public IData mod(IData other) { + throw new UnsupportedOperationException("Cannot perform modulo on bool values"); + } + + @Override + public IData and(IData other) { + return new DataBool(value & other.asBool()); + } + + @Override + public IData or(IData other) { + return new DataBool(value | other.asBool()); + } + + @Override + public IData xor(IData other) { + return new DataBool(value ^ other.asBool()); + } + + @Override + public IData neg() { + throw new UnsupportedOperationException("Cannot negate a bool value"); + } + + @Override + public IData not() { + return new DataBool(!value); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataByte.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataByte.java new file mode 100644 index 0000000..c5bc3b0 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataByte.java @@ -0,0 +1,187 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.data; + +import java.util.List; +import java.util.Map; + +/** + * + * @author Stan + */ +public class DataByte implements IData { + private final byte value; + + public DataByte(byte value) { + this.value = value; + } + + @Override + public boolean asBool() { + throw new RuntimeException("Cannot cast a byte to a bool"); + } + + @Override + public byte asByte() { + return value; + } + + @Override + public short asShort() { + return value; + } + + @Override + public int asInt() { + return value; + } + + @Override + public long asLong() { + return value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public String asString() { + return Byte.toString(value); + } + + @Override + public List asList() { + throw new UnsupportedOperationException("Cannot convert a byte to a list"); + } + + @Override + public Map asMap() { + throw new UnsupportedOperationException("Cannot convert a byte to a map"); + } + + @Override + public byte[] asByteArray() { + throw new RuntimeException("Cannot cast a byte to a byte array"); + } + + @Override + public int[] asIntArray() { + throw new RuntimeException("Cannot cast a byte to an int array"); + } + + @Override + public IData getAt(int i) { + throw new RuntimeException("A byte is not indexable"); + } + + @Override + public void setAt(int i, IData value) { + throw new RuntimeException("A byte is not indexable"); + } + + @Override + public IData memberGet(String name) { + throw new RuntimeException("A byte is not indexable"); + } + + @Override + public void memberSet(String name, IData data) { + throw new RuntimeException("A byte is not indexable"); + } + + @Override + public int length() { + return 0; + } + + @Override + public boolean contains(IData data) { + return data.asByte() == value; + } + + @Override + public boolean equals(IData data) { + return value == data.asByte(); + } + + @Override + public int compareTo(IData data) { + return Byte.compare(value, data.asByte()); + } + + @Override + public IData immutable() { + return this; + } + + @Override + public IData update(IData data) { + return data; + } + + @Override + public T convert(IDataConverter converter) { + return converter.fromByte(value); + } + + @Override + public IData add(IData other) { + return new DataByte((byte) (value + other.asByte())); + } + + @Override + public IData sub(IData other) { + return new DataByte((byte) (value - other.asByte())); + } + + @Override + public IData mul(IData other) { + return new DataByte((byte) (value * other.asByte())); + } + + @Override + public IData div(IData other) { + return new DataByte((byte) (value / other.asByte())); + } + + @Override + public IData mod(IData other) { + return new DataByte((byte) (value % other.asByte())); + } + + @Override + public IData and(IData other) { + return new DataByte((byte) (value & other.asByte())); + } + + @Override + public IData or(IData other) { + return new DataByte((byte) (value | other.asByte())); + } + + @Override + public IData xor(IData other) { + return new DataByte((byte) (value ^ other.asByte())); + } + + @Override + public IData neg() { + return new DataByte((byte) (- value)); + } + + @Override + public IData not() { + return new DataByte((byte) (~ value)); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataByteArray.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataByteArray.java new file mode 100644 index 0000000..cedaa58 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataByteArray.java @@ -0,0 +1,220 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.data; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * + * @author Stan + */ +public class DataByteArray implements IData { + private final byte[] data; + private final boolean immutable; + + public DataByteArray(byte[] data, boolean immutable) { + this.data = data; + this.immutable = immutable; + } + + @Override + public IData add(IData other) { + byte[] otherData = other.asByteArray(); + byte[] result = Arrays.copyOf(data, data.length + otherData.length); + System.arraycopy(otherData, 0, result, data.length, otherData.length); + return new DataByteArray(result, immutable); + } + + @Override + public IData sub(IData other) { + throw new UnsupportedOperationException("Cannot subtract from a byte array"); + } + + @Override + public IData mul(IData other) { + throw new UnsupportedOperationException("Cannot multiply with an arary"); + } + + @Override + public IData div(IData other) { + throw new UnsupportedOperationException("Cannot divide an array"); + } + + @Override + public IData mod(IData other) { + throw new UnsupportedOperationException("Cannot perform modulo on an array"); + } + + @Override + public IData and(IData other) { + throw new UnsupportedOperationException("Arrays don't support bitwise operations"); + } + + @Override + public IData or(IData other) { + throw new UnsupportedOperationException("Arrays don't support bitwise operations"); + } + + @Override + public IData xor(IData other) { + throw new UnsupportedOperationException("Arrays don't support bitwise operations"); + } + + @Override + public IData neg() { + throw new UnsupportedOperationException("Cannot negate arrays"); + } + + @Override + public IData not() { + throw new UnsupportedOperationException("Arrays don't support bitwise operations"); + } + + @Override + public boolean asBool() { + throw new UnsupportedOperationException("Cannot cast an array to bool"); + } + + @Override + public byte asByte() { + throw new UnsupportedOperationException("Cannot cast an array to byte"); + } + + @Override + public short asShort() { + throw new UnsupportedOperationException("Cannot cast an array to short"); + } + + @Override + public int asInt() { + throw new UnsupportedOperationException("Cannot cast an array to int"); + } + + @Override + public long asLong() { + throw new UnsupportedOperationException("Cannot cast an array to long"); + } + + @Override + public float asFloat() { + throw new UnsupportedOperationException("Cannot cast an array to float"); + } + + @Override + public double asDouble() { + throw new UnsupportedOperationException("Cannot cast an array to double"); + } + + @Override + public String asString() { + StringBuilder result = new StringBuilder(); + result.append("X\""); + for (byte value : data) { + String str = Integer.toHexString(value); + if (str.length() == 1) { + result.append('0'); + } + result.append(str); + } + result.append('"'); + return result.toString(); + } + + @Override + public List asList() { + List result = new ArrayList(); + for (byte value : data) { + result.add(new DataByte(value)); + } + return result; + } + + @Override + public Map asMap() { + throw new UnsupportedOperationException("Cannot convert list to map"); + } + + @Override + public byte[] asByteArray() { + return data; + } + + @Override + public int[] asIntArray() { + int[] result = new int[data.length]; + for (int i = 0; i < result.length; i++) { + result[i] = data[i]; + } + return result; + } + + @Override + public IData getAt(int i) { + return new DataByte(data[i]); + } + + @Override + public void setAt(int i, IData value) { + if (immutable) { + throw new UnsupportedOperationException("Cannot modify this byte array"); + } else { + data[i] = value.asByte(); + } + } + + @Override + public IData memberGet(String name) { + throw new UnsupportedOperationException("byte[] doesn't have members"); + } + + @Override + public void memberSet(String name, IData data) { + throw new UnsupportedOperationException("cannot set byte[] members"); + } + + @Override + public int length() { + return data.length; + } + + @Override + public boolean contains(IData data) { + return equals(data); + } + + @Override + public int compareTo(IData data) { + throw new UnsupportedOperationException("Cannot compare arrays"); + } + + @Override + public boolean equals(IData data) { + return Arrays.equals(this.data, data.asByteArray()); + } + + @Override + public IData immutable() { + if (immutable) { + return this; + } else { + return new DataByteArray(Arrays.copyOf(this.data, this.data.length), true); + } + } + + @Override + public IData update(IData data) { + return data; + } + + @Override + public T convert(IDataConverter converter) { + return converter.fromByteArray(this.data); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataDouble.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataDouble.java new file mode 100644 index 0000000..ceb53b4 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataDouble.java @@ -0,0 +1,187 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.data; + +import java.util.List; +import java.util.Map; + +/** + * + * @author Stan + */ +public class DataDouble implements IData { + private final double value; + + public DataDouble(double value) { + this.value = value; + } + + @Override + public boolean asBool() { + throw new RuntimeException("Cannot cast a double to a bool"); + } + + @Override + public byte asByte() { + return (byte) value; + } + + @Override + public short asShort() { + return (short) value; + } + + @Override + public int asInt() { + return (int) value; + } + + @Override + public long asLong() { + return (long) value; + } + + @Override + public float asFloat() { + return (float) value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public String asString() { + return Double.toString(value); + } + + @Override + public List asList() { + throw new UnsupportedOperationException("Cannot cast a double to a list"); + } + + @Override + public Map asMap() { + throw new UnsupportedOperationException("Cannot cast a double to a map"); + } + + @Override + public byte[] asByteArray() { + throw new RuntimeException("Cannot cast a double to a byte array"); + } + + @Override + public int[] asIntArray() { + throw new RuntimeException("Cannot cast a double to an int array"); + } + + @Override + public IData getAt(int i) { + throw new RuntimeException("A double is not indexable"); + } + + @Override + public void setAt(int i, IData value) { + throw new RuntimeException("A double is not indexable"); + } + + @Override + public IData memberGet(String name) { + throw new RuntimeException("A double is not indexable"); + } + + @Override + public void memberSet(String name, IData data) { + throw new RuntimeException("A double is not indexable"); + } + + @Override + public int length() { + return 0; + } + + @Override + public boolean contains(IData data) { + return data.asDouble() == value; + } + + @Override + public boolean equals(IData other) { + return value == other.asDouble(); + } + + @Override + public int compareTo(IData other) { + return Double.compare(value, other.asDouble()); + } + + @Override + public IData immutable() { + return this; + } + + @Override + public IData update(IData data) { + return data; + } + + @Override + public T convert(IDataConverter converter) { + return converter.fromDouble(value); + } + + @Override + public IData add(IData other) { + return new DataDouble(value + other.asDouble()); + } + + @Override + public IData sub(IData other) { + return new DataDouble(value - other.asDouble()); + } + + @Override + public IData mul(IData other) { + return new DataDouble(value * other.asDouble()); + } + + @Override + public IData div(IData other) { + return new DataDouble(value / other.asDouble()); + } + + @Override + public IData mod(IData other) { + return new DataDouble(value % other.asDouble()); + } + + @Override + public IData and(IData other) { + throw new UnsupportedOperationException("Cannot perform bitwise operations on double"); + } + + @Override + public IData or(IData other) { + throw new UnsupportedOperationException("Cannot perform bitwise operations on double"); + } + + @Override + public IData xor(IData other) { + throw new UnsupportedOperationException("Cannot perform bitwise operations on double"); + } + + @Override + public IData neg() { + return new DataDouble(- value); + } + + @Override + public IData not() { + throw new UnsupportedOperationException("Cannot perform bitwise operations on double"); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataFloat.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataFloat.java new file mode 100644 index 0000000..f92a5d6 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataFloat.java @@ -0,0 +1,187 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.data; + +import java.util.List; +import java.util.Map; + +/** + * + * @author Stan + */ +public class DataFloat implements IData { + private final float value; + + public DataFloat(float value) { + this.value = value; + } + + @Override + public boolean asBool() { + throw new RuntimeException("Cannot cast a float to a bool"); + } + + @Override + public byte asByte() { + return (byte) value; + } + + @Override + public short asShort() { + return (short) value; + } + + @Override + public int asInt() { + return (int) value; + } + + @Override + public long asLong() { + return (long) value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public String asString() { + return Float.toString(value); + } + + @Override + public List asList() { + throw new UnsupportedOperationException("Cannot cast a float to a list"); + } + + @Override + public Map asMap() { + throw new UnsupportedOperationException("Cannot cast a float to a map"); + } + + @Override + public byte[] asByteArray() { + throw new RuntimeException("Cannot cast a float to a byte array"); + } + + @Override + public int[] asIntArray() { + throw new RuntimeException("Cannot cast a float to an int array"); + } + + @Override + public IData getAt(int i) { + throw new RuntimeException("A float is not indexable"); + } + + @Override + public void setAt(int i, IData value) { + throw new RuntimeException("A float is not indexable"); + } + + @Override + public IData memberGet(String name) { + throw new RuntimeException("A float is not indexable"); + } + + @Override + public void memberSet(String name, IData data) { + throw new RuntimeException("A float is not indexable"); + } + + @Override + public int length() { + return 0; + } + + @Override + public boolean contains(IData data) { + return data.asFloat() == value; + } + + @Override + public boolean equals(IData data) { + return value == data.asFloat(); + } + + @Override + public int compareTo(IData data) { + return Float.compare(value, data.asFloat()); + } + + @Override + public IData immutable() { + return this; + } + + @Override + public IData update(IData data) { + return data; + } + + @Override + public T convert(IDataConverter converter) { + return converter.fromFloat(value); + } + + @Override + public IData add(IData other) { + return new DataFloat(value + other.asFloat()); + } + + @Override + public IData sub(IData other) { + return new DataFloat(value - other.asFloat()); + } + + @Override + public IData mul(IData other) { + return new DataFloat(value * other.asFloat()); + } + + @Override + public IData div(IData other) { + return new DataFloat(value / other.asFloat()); + } + + @Override + public IData mod(IData other) { + return new DataFloat(value % other.asFloat()); + } + + @Override + public IData and(IData other) { + throw new UnsupportedOperationException("Cannot perform bitwise operations on float"); + } + + @Override + public IData or(IData other) { + throw new UnsupportedOperationException("Cannot perform bitwise operations on float"); + } + + @Override + public IData xor(IData other) { + throw new UnsupportedOperationException("Cannot perform bitwise operations on float"); + } + + @Override + public IData neg() { + return new DataFloat(- value); + } + + @Override + public IData not() { + throw new UnsupportedOperationException("Cannot perform bitwise operations on float"); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataInt.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataInt.java new file mode 100644 index 0000000..14d14e4 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataInt.java @@ -0,0 +1,187 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.data; + +import java.util.List; +import java.util.Map; + +/** + * + * @author Stan + */ +public class DataInt implements IData { + private final int value; + + public DataInt(int value) { + this.value = value; + } + + @Override + public boolean asBool() { + throw new RuntimeException("Cannot cast an int to a bool"); + } + + @Override + public byte asByte() { + return (byte) value; + } + + @Override + public short asShort() { + return (short) value; + } + + @Override + public int asInt() { + return value; + } + + @Override + public long asLong() { + return value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public String asString() { + return Integer.toString(value); + } + + @Override + public List asList() { + throw new UnsupportedOperationException("Cannot cast an int to a list"); + } + + @Override + public Map asMap() { + throw new UnsupportedOperationException("Cannot cast an int to a map"); + } + + @Override + public byte[] asByteArray() { + throw new RuntimeException("Cannot cast an int to a byte array"); + } + + @Override + public int[] asIntArray() { + throw new RuntimeException("Cannot cast an int to an int array"); + } + + @Override + public IData getAt(int i) { + throw new RuntimeException("An int is not indexable"); + } + + @Override + public void setAt(int i, IData value) { + throw new RuntimeException("An int is not indexable"); + } + + @Override + public IData memberGet(String name) { + throw new RuntimeException("An int is not indexable"); + } + + @Override + public void memberSet(String name, IData data) { + throw new RuntimeException("An int is not indexable"); + } + + @Override + public int length() { + return 0; + } + + @Override + public boolean contains(IData data) { + return data.asInt() == value; + } + + @Override + public boolean equals(IData data) { + return value == data.asInt(); + } + + @Override + public int compareTo(IData data) { + return Integer.compare(value, data.asInt()); + } + + @Override + public IData immutable() { + return this; + } + + @Override + public IData update(IData data) { + return data; + } + + @Override + public T convert(IDataConverter converter) { + return converter.fromInt(value); + } + + @Override + public IData add(IData other) { + return new DataInt(value + other.asInt()); + } + + @Override + public IData sub(IData other) { + return new DataInt(value - other.asInt()); + } + + @Override + public IData mul(IData other) { + return new DataInt(value * other.asInt()); + } + + @Override + public IData div(IData other) { + return new DataInt(value / other.asInt()); + } + + @Override + public IData mod(IData other) { + return new DataInt(value % other.asInt()); + } + + @Override + public IData and(IData other) { + return new DataInt(value & other.asInt()); + } + + @Override + public IData or(IData other) { + return new DataInt(value | other.asInt()); + } + + @Override + public IData xor(IData other) { + return new DataInt(value ^ other.asInt()); + } + + @Override + public IData neg() { + return new DataInt(- value); + } + + @Override + public IData not() { + return new DataInt(~ value); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataIntArray.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataIntArray.java new file mode 100644 index 0000000..a3fc47f --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataIntArray.java @@ -0,0 +1,222 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.data; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * + * @author Stan + */ +public class DataIntArray implements IData { + private final int[] data; + private final boolean immutable; + + public DataIntArray(int[] data, boolean immutable) { + this.data = data; + this.immutable = immutable; + } + + @Override + public IData add(IData other) { + int[] otherData = other.asIntArray(); + int[] result = Arrays.copyOf(data, data.length + otherData.length); + System.arraycopy(otherData, 0, result, data.length, otherData.length); + return new DataIntArray(result, immutable); + } + + @Override + public IData sub(IData other) { + throw new UnsupportedOperationException("Cannot subtract from an int array"); + } + + @Override + public IData mul(IData other) { + throw new UnsupportedOperationException("Cannot multiply with an arary"); + } + + @Override + public IData div(IData other) { + throw new UnsupportedOperationException("Cannot divide an array"); + } + + @Override + public IData mod(IData other) { + throw new UnsupportedOperationException("Cannot perform modulo on an array"); + } + + @Override + public IData and(IData other) { + throw new UnsupportedOperationException("Arrays don't support bitwise operations"); + } + + @Override + public IData or(IData other) { + throw new UnsupportedOperationException("Arrays don't support bitwise operations"); + } + + @Override + public IData xor(IData other) { + throw new UnsupportedOperationException("Arrays don't support bitwise operations"); + } + + @Override + public IData neg() { + throw new UnsupportedOperationException("Cannot negate arrays"); + } + + @Override + public IData not() { + throw new UnsupportedOperationException("Arrays don't support bitwise operations"); + } + + @Override + public boolean asBool() { + throw new UnsupportedOperationException("Cannot cast an array to bool"); + } + + @Override + public byte asByte() { + throw new UnsupportedOperationException("Cannot cast an array to byte"); + } + + @Override + public short asShort() { + throw new UnsupportedOperationException("Cannot cast an array to short"); + } + + @Override + public int asInt() { + throw new UnsupportedOperationException("Cannot cast an array to int"); + } + + @Override + public long asLong() { + throw new UnsupportedOperationException("Cannot cast an array to long"); + } + + @Override + public float asFloat() { + throw new UnsupportedOperationException("Cannot cast an array to float"); + } + + @Override + public double asDouble() { + throw new UnsupportedOperationException("Cannot cast an array to double"); + } + + @Override + public String asString() { + StringBuilder result = new StringBuilder(); + result.append('['); + boolean first = true; + for (int value : data) { + if (first) { + first = false; + } else { + result.append(", "); + } + result.append(value); + } + result.append(']'); + return result.toString(); + } + + @Override + public List asList() { + List result = new ArrayList(); + for (int value : data) { + result.add(new DataInt(value)); + } + return result; + } + + @Override + public Map asMap() { + throw new UnsupportedOperationException("Cannot convert list to map"); + } + + @Override + public byte[] asByteArray() { + byte[] result = new byte[data.length]; + for (int i = 0; i < result.length; i++) { + result[i] = (byte) data[i]; + } + return result; + } + + @Override + public int[] asIntArray() { + return data; + } + + @Override + public IData getAt(int i) { + return new DataInt(data[i]); + } + + @Override + public void setAt(int i, IData value) { + if (immutable) { + throw new UnsupportedOperationException("Cannot modify this byte array"); + } else { + data[i] = value.asInt(); + } + } + + @Override + public IData memberGet(String name) { + throw new UnsupportedOperationException("int[] doesn't have members"); + } + + @Override + public void memberSet(String name, IData data) { + throw new UnsupportedOperationException("cannot set int[] members"); + } + + @Override + public int length() { + return data.length; + } + + @Override + public boolean contains(IData data) { + return equals(data); + } + + @Override + public int compareTo(IData data) { + throw new UnsupportedOperationException("Cannot compare arrays"); + } + + @Override + public boolean equals(IData data) { + return Arrays.equals(this.data, data.asIntArray()); + } + + @Override + public IData immutable() { + if (immutable) { + return this; + } else { + return new DataIntArray(Arrays.copyOf(this.data, this.data.length), true); + } + } + + @Override + public IData update(IData data) { + return data; + } + + @Override + public T convert(IDataConverter converter) { + return converter.fromIntArray(this.data); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataList.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataList.java new file mode 100644 index 0000000..3d61d42 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataList.java @@ -0,0 +1,243 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.data; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * + * @author Stan + */ +public class DataList implements IData { + private final List values; + private final boolean immutable; + + public DataList(List values, boolean immutable) { + this.values = values; + this.immutable = immutable; + } + + @Override + public boolean asBool() { + throw new RuntimeException("Cannot convert a list to a bool"); + } + + @Override + public byte asByte() { + throw new RuntimeException("Cannot convert a list to a byte"); + } + + @Override + public short asShort() { + throw new RuntimeException("Cannot convert a list to a short"); + } + + @Override + public int asInt() { + throw new RuntimeException("Cannot convert a list to an int"); + } + + @Override + public long asLong() { + throw new RuntimeException("Cannot convert a list to a long"); + } + + @Override + public float asFloat() { + throw new RuntimeException("Cannot convert a list to a float"); + } + + @Override + public double asDouble() { + throw new RuntimeException("Cannot convert a list to a double"); + } + + @Override + public List asList() { + if (immutable) { + return Collections.unmodifiableList(values); + } else { + return values; + } + } + + @Override + public Map asMap() { + throw new RuntimeException("Cannot convert a list to a map"); + } + + @Override + public String asString() { + StringBuilder output = new StringBuilder(); + output.append('['); + boolean first = true; + for (IData value : values) { + if (first) { + first = false; + } else { + output.append(", "); + } + output.append(value.asString()); + } + output.append(']'); + return output.toString(); + } + + @Override + public byte[] asByteArray() { + byte[] result = new byte[values.size()]; + for (int i = 0; i < values.size(); i++) { + result[i] = values.get(i).asByte(); + } + return result; + } + + @Override + public int[] asIntArray() { + int[] result = new int[values.size()]; + for (int i = 0; i < values.size(); i++) { + result[i] = values.get(i).asInt(); + } + return result; + } + + @Override + public IData getAt(int i) { + return values.get(i); + } + + @Override + public void setAt(int i, IData value) { + if (immutable) { + throw new RuntimeException("this list is immutable"); + } else { + values.set(i, value); + } + } + + @Override + public IData memberGet(String name) { + throw new RuntimeException("Lists don't have members"); + } + + @Override + public void memberSet(String name, IData data) { + throw new RuntimeException("Lists don't have members"); + } + + @Override + public int length() { + return values.size(); + } + + @Override + public boolean contains(IData data) { + for (IData value : values) { + if (value.equals(data)) return true; + } + + return false; + } + + @Override + public boolean equals(IData data) { + List otherValues = data.asList(); + if (otherValues.size() != values.size()) return false; + + for (int i = 0; i < values.size(); i++) { + if (!values.get(i).equals(otherValues.get(i))) return false; + } + + return true; + } + + @Override + public int compareTo(IData data) { + throw new UnsupportedOperationException("Cannot compare with a list"); + } + + @Override + public IData immutable() { + if (immutable) { + return this; + } else { + List copy = new ArrayList(); + for (IData value : values) { + copy.add(value.immutable()); + } + return new DataList(copy, true); + } + } + + @Override + public IData update(IData data) { + if (immutable) data = data.immutable(); + + List result = new ArrayList(); + result.addAll(values); + result.addAll(data.asList()); + return new DataList(result, immutable); + } + + @Override + public T convert(IDataConverter converter) { + return converter.fromList(values); + } + + @Override + public IData add(IData other) { + return update(other); + } + + @Override + public IData sub(IData other) { + throw new UnsupportedOperationException("Cannot subtract from a list"); + } + + @Override + public IData mul(IData other) { + throw new UnsupportedOperationException("Cannot multiply a list"); + } + + @Override + public IData div(IData other) { + throw new UnsupportedOperationException("Cannot divide a list"); + } + + @Override + public IData mod(IData other) { + throw new UnsupportedOperationException("Cannot perform modulo on a list"); + } + + @Override + public IData and(IData other) { + throw new UnsupportedOperationException("Lists don't support bitwise operators"); + } + + @Override + public IData or(IData other) { + throw new UnsupportedOperationException("Lists don't support bitwise operators"); + } + + @Override + public IData xor(IData other) { + throw new UnsupportedOperationException("Lists don't support bitwise operators"); + } + + @Override + public IData neg() { + throw new UnsupportedOperationException("Cannot negate a list"); + } + + @Override + public IData not() { + throw new UnsupportedOperationException("Lists don't support bitwise operators"); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataLong.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataLong.java new file mode 100644 index 0000000..573980c --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataLong.java @@ -0,0 +1,187 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.data; + +import java.util.List; +import java.util.Map; + +/** + * + * @author Stan + */ +public class DataLong implements IData { + private final long value; + + public DataLong(long value) { + this.value = value; + } + + @Override + public boolean asBool() { + throw new RuntimeException("Cannot cast a long to a bool"); + } + + @Override + public byte asByte() { + return (byte) value; + } + + @Override + public short asShort() { + return (short) value; + } + + @Override + public int asInt() { + return (int) value; + } + + @Override + public long asLong() { + return value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public String asString() { + return Long.toString(value); + } + + @Override + public List asList() { + throw new UnsupportedOperationException("Cannot cast a long to a list"); + } + + @Override + public Map asMap() { + throw new UnsupportedOperationException("Cannot cast a long to a map"); + } + + @Override + public byte[] asByteArray() { + throw new RuntimeException("Cannot cast a long to a byte array"); + } + + @Override + public int[] asIntArray() { + throw new RuntimeException("Cannot cast a long to an int array"); + } + + @Override + public IData getAt(int i) { + throw new RuntimeException("A long is not indexable"); + } + + @Override + public void setAt(int i, IData value) { + throw new RuntimeException("A long is not indexable"); + } + + @Override + public IData memberGet(String name) { + throw new RuntimeException("A long is not indexable"); + } + + @Override + public void memberSet(String name, IData data) { + throw new RuntimeException("A long is not indexable"); + } + + @Override + public int length() { + return 0; + } + + @Override + public boolean contains(IData data) { + return data.asLong() == value; + } + + @Override + public boolean equals(IData data) { + return value == data.asLong(); + } + + @Override + public int compareTo(IData data) { + return Long.compare(value, data.asLong()); + } + + @Override + public IData immutable() { + return this; + } + + @Override + public IData update(IData data) { + return data; + } + + @Override + public T convert(IDataConverter converter) { + return converter.fromLong(value); + } + + @Override + public IData add(IData other) { + return new DataLong(value + other.asLong()); + } + + @Override + public IData sub(IData other) { + return new DataLong(value - other.asLong()); + } + + @Override + public IData mul(IData other) { + return new DataLong(value * other.asLong()); + } + + @Override + public IData div(IData other) { + return new DataLong(value / other.asLong()); + } + + @Override + public IData mod(IData other) { + return new DataLong(value % other.asLong()); + } + + @Override + public IData and(IData other) { + return new DataLong(value & other.asLong()); + } + + @Override + public IData or(IData other) { + return new DataLong(value | other.asLong()); + } + + @Override + public IData xor(IData other) { + return new DataLong(value ^ other.asLong()); + } + + @Override + public IData neg() { + return new DataLong(- value); + } + + @Override + public IData not() { + return new DataLong(~ value); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataMap.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataMap.java new file mode 100644 index 0000000..f046a41 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataMap.java @@ -0,0 +1,272 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.data; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * + * @author Stan + */ +public class DataMap implements IData { + private final Map data; + private final boolean immutable; + + public DataMap(Map data, boolean immutable) { + this.data = data; + this.immutable = immutable; + } + + @Override + public IData add(IData other) { + Map result = new HashMap(); + Map otherMap = other.asMap(); + + for (Map.Entry entry : data.entrySet()) { + result.put(entry.getKey(), entry.getValue()); + } + for (Map.Entry entry : otherMap.entrySet()) { + result.put(entry.getKey(), entry.getValue()); + } + + return new DataMap(result, immutable); + } + + @Override + public IData sub(IData other) { + Map result = new HashMap(); + Map otherMap = other.asMap(); + + for (Map.Entry entry : data.entrySet()) { + result.put(entry.getKey(), entry.getValue()); + } + for (String key : otherMap.keySet()) { + result.remove(key); + } + + return new DataMap(result, immutable); + } + + @Override + public IData mul(IData other) { + throw new UnsupportedOperationException("Cannot multiply maps"); + } + + @Override + public IData div(IData other) { + throw new UnsupportedOperationException("Cannot divide maps"); + } + + @Override + public IData mod(IData other) { + throw new UnsupportedOperationException("Cannot perform modulo with maps"); + } + + @Override + public IData and(IData other) { + throw new UnsupportedOperationException("Maps do not support bitwise operations"); + } + + @Override + public IData or(IData other) { + throw new UnsupportedOperationException("Maps do not support bitwise operations"); + } + + @Override + public IData xor(IData other) { + throw new UnsupportedOperationException("Maps do not support bitwise operations"); + } + + @Override + public IData neg() { + throw new UnsupportedOperationException("Cannot negate maps."); + } + + @Override + public IData not() { + throw new UnsupportedOperationException("Maps do not support bitwise operations"); + } + + @Override + public boolean asBool() { + throw new UnsupportedOperationException("Cannot cast map to bool"); + } + + @Override + public byte asByte() { + throw new UnsupportedOperationException("Cannot cast map to byte"); + } + + @Override + public short asShort() { + throw new UnsupportedOperationException("Cannot cast map to short"); + } + + @Override + public int asInt() { + throw new UnsupportedOperationException("Cannot cast map to int"); + } + + @Override + public long asLong() { + throw new UnsupportedOperationException("Cannot cast map to long"); + } + + @Override + public float asFloat() { + throw new UnsupportedOperationException("Cannot cast map to float"); + } + + @Override + public double asDouble() { + throw new UnsupportedOperationException("Cannot cast map to double"); + } + + @Override + public String asString() { + StringBuilder result = new StringBuilder(); + result.append('{'); + boolean first = false; + for (Map.Entry entry : data.entrySet()) { + if (first) { + first = false; + } else { + result.append(", "); + } + result.append(entry.getKey()); + result.append(": "); + result.append(entry.getValue().asString()); + } + result.append('}'); + return result.toString(); + } + + @Override + public List asList() { + throw new UnsupportedOperationException("Cannot cast map to list"); + } + + @Override + public Map asMap() { + if (immutable) { + return Collections.unmodifiableMap(data); + } else { + return data; + } + } + + @Override + public byte[] asByteArray() { + throw new UnsupportedOperationException("Cannot cast map to byte[]"); + } + + @Override + public int[] asIntArray() { + throw new UnsupportedOperationException("Cannot cast map to int[]"); + } + + @Override + public IData getAt(int i) { + throw new UnsupportedOperationException("Cannot index maps with integers"); + } + + @Override + public void setAt(int i, IData value) { + throw new UnsupportedOperationException("Cannot index maps with integers"); + } + + @Override + public IData memberGet(String name) { + return data.get(name); + } + + @Override + public void memberSet(String name, IData data) { + if (immutable) { + throw new UnsupportedOperationException("this map is not modifiable"); + } else { + this.data.put(name, data); + } + } + + @Override + public int length() { + return data.size(); + } + + @Override + public boolean contains(IData data) { + Map dataMap = data.asMap(); + for (Map.Entry dataEntry : dataMap.entrySet()) { + if (!this.data.containsKey(dataEntry.getKey())) { + return false; + } else if (!this.data.get(dataEntry.getKey()).contains(data)) { + return false; + } + } + + return true; + } + + @Override + public int compareTo(IData data) { + throw new UnsupportedOperationException("Cannot compare maps"); + } + + @Override + public boolean equals(IData data) { + if (this == data) return true; + + Map dataMap = data.asMap(); + if (dataMap.size() != this.data.size()) return false; + + for (Map.Entry dataEntry : this.data.entrySet()) { + if (!dataMap.containsKey(dataEntry.getKey())) { + return false; + } else if (!dataMap.get(dataEntry.getKey()).equals(dataEntry.getValue())) { + return false; + } + } + + return true; + } + + @Override + public IData immutable() { + if (immutable) { + return this; + } else { + Map result = new HashMap(); + for (Map.Entry entry : this.data.entrySet()) { + result.put(entry.getKey(), entry.getValue().immutable()); + } + return new DataMap(result, true); + } + } + + @Override + public IData update(IData data) { + if (immutable) data = data.immutable(); + + Map result = new HashMap(); + for (Map.Entry entry : this.data.entrySet()) { + result.put(entry.getKey(), entry.getValue()); + } + for (Map.Entry entry : data.asMap().entrySet()) { + result.put(entry.getKey(), entry.getValue()); + } + return new DataMap(result, immutable); + } + + @Override + public T convert(IDataConverter converter) { + return converter.fromMap(data); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataShort.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataShort.java new file mode 100644 index 0000000..22466f1 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataShort.java @@ -0,0 +1,187 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.data; + +import java.util.List; +import java.util.Map; + +/** + * + * @author Stan + */ +public class DataShort implements IData { + private final short value; + + public DataShort(short value) { + this.value = value; + } + + @Override + public boolean asBool() { + throw new RuntimeException("Cannot cast a short to a bool"); + } + + @Override + public byte asByte() { + return (byte) value; + } + + @Override + public short asShort() { + return value; + } + + @Override + public int asInt() { + return value; + } + + @Override + public long asLong() { + return value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public String asString() { + return Short.toString(value); + } + + @Override + public List asList() { + throw new UnsupportedOperationException("Cannot cast a short to a list"); + } + + @Override + public Map asMap() { + throw new UnsupportedOperationException("Cannot cast a short to a map"); + } + + @Override + public byte[] asByteArray() { + throw new RuntimeException("Cannot cast a short to a byte array"); + } + + @Override + public int[] asIntArray() { + throw new RuntimeException("Cannot cast a short to an int array"); + } + + @Override + public IData getAt(int i) { + throw new RuntimeException("A short is not indexable"); + } + + @Override + public void setAt(int i, IData value) { + throw new RuntimeException("A short is not indexable"); + } + + @Override + public IData memberGet(String name) { + throw new RuntimeException("A short is not indexable"); + } + + @Override + public void memberSet(String name, IData data) { + throw new RuntimeException("A short is not indexable"); + } + + @Override + public int length() { + return 0; + } + + @Override + public boolean contains(IData data) { + return data.asShort() == value; + } + + @Override + public boolean equals(IData data) { + return value == data.asShort(); + } + + @Override + public int compareTo(IData data) { + return Short.compare(value, data.asShort()); + } + + @Override + public IData immutable() { + return this; + } + + @Override + public IData update(IData data) { + return data; + } + + @Override + public T convert(IDataConverter converter) { + return converter.fromShort(value); + } + + @Override + public IData add(IData other) { + return new DataShort((short) (value + other.asShort())); + } + + @Override + public IData sub(IData other) { + return new DataShort((short) (value - other.asShort())); + } + + @Override + public IData mul(IData other) { + return new DataShort((short) (value * other.asShort())); + } + + @Override + public IData div(IData other) { + return new DataShort((short) (value / other.asShort())); + } + + @Override + public IData mod(IData other) { + return new DataShort((short) (value % other.asShort())); + } + + @Override + public IData and(IData other) { + return new DataShort((short) (value & other.asShort())); + } + + @Override + public IData or(IData other) { + return new DataShort((short) (value | other.asShort())); + } + + @Override + public IData xor(IData other) { + return new DataShort((short) (value ^ other.asShort())); + } + + @Override + public IData neg() { + return new DataShort((short) (- value)); + } + + @Override + public IData not() { + return new DataShort((short) (~ value)); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataString.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataString.java new file mode 100644 index 0000000..353e8ed --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/DataString.java @@ -0,0 +1,191 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.data; + +import java.util.List; +import java.util.Map; + +/** + * + * @author Stan + */ +public class DataString implements IData { + private final String value; + + public DataString(String value) { + this.value = value; + } + + @Override + public boolean asBool() { + return Boolean.parseBoolean(value); + } + + @Override + public byte asByte() { + return Byte.parseByte(value); + } + + @Override + public short asShort() { + return Short.parseShort(value); + } + + @Override + public int asInt() { + return Integer.parseInt(value); + } + + @Override + public long asLong() { + return Long.parseLong(value); + } + + @Override + public float asFloat() { + return Float.parseFloat(value); + } + + @Override + public double asDouble() { + return Double.parseDouble(value); + } + + @Override + public String asString() { + return value; + } + + @Override + public List asList() { + throw new UnsupportedOperationException("Cannot cast a string to a list"); + } + + @Override + public Map asMap() { + throw new UnsupportedOperationException("Cannot cast a string to a map"); + } + + @Override + public byte[] asByteArray() { + throw new RuntimeException("Cannot cast a string to a byte array"); + } + + @Override + public int[] asIntArray() { + throw new RuntimeException("Cannot cast a string to an int array"); + } + + @Override + public IData getAt(int i) { + return new DataString(value.substring(i, i + 1)); + } + + @Override + public void setAt(int i, IData value) { + throw new RuntimeException("Strings are immutable"); + } + + @Override + public IData memberGet(String name) { + if (name.equals("length")) { + return new DataInt(value.length()); + } else { + throw new RuntimeException("no such member: " + name); + } + } + + @Override + public void memberSet(String name, IData data) { + throw new RuntimeException("Strings are immutable"); + } + + @Override + public int length() { + return value.length(); + } + + @Override + public boolean contains(IData data) { + return data.asString().equals(value); + } + + @Override + public boolean equals(IData data) { + return value.equals(data.asString()); + } + + @Override + public int compareTo(IData data) { + return value.compareTo(data.asString()); + } + + @Override + public IData immutable() { + return this; + } + + @Override + public IData update(IData data) { + return data; + } + + @Override + public T convert(IDataConverter converter) { + return converter.fromString(value); + } + + @Override + public IData add(IData other) { + return new DataString(value + other.asString()); + } + + @Override + public IData sub(IData other) { + throw new UnsupportedOperationException("Cannot subtract from a string"); + } + + @Override + public IData mul(IData other) { + throw new UnsupportedOperationException("Cannot multiply a string"); + } + + @Override + public IData div(IData other) { + throw new UnsupportedOperationException("Cannot divide a string"); + } + + @Override + public IData mod(IData other) { + throw new UnsupportedOperationException("Cannot perform modulo on a string"); + } + + @Override + public IData and(IData other) { + throw new UnsupportedOperationException("Cannot perform bitwise arithmetic on a string"); + } + + @Override + public IData or(IData other) { + throw new UnsupportedOperationException("Cannot perform bitwise arithmetic on a string"); + } + + @Override + public IData xor(IData other) { + throw new UnsupportedOperationException("Cannot perform bitwise arithmetic on a string"); + } + + @Override + public IData neg() { + throw new UnsupportedOperationException("Cannot negate a string"); + } + + @Override + public IData not() { + throw new UnsupportedOperationException("Cannot perform bitwise arithmetic on a string"); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/IData.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/IData.java new file mode 100644 index 0000000..6ae5fa7 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/IData.java @@ -0,0 +1,123 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.data; + +import java.util.List; +import java.util.Map; +import stanhebben.zenscript.annotations.ZenCaster; +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenGetter; +import stanhebben.zenscript.annotations.ZenMemberGetter; +import stanhebben.zenscript.annotations.ZenMemberSetter; +import stanhebben.zenscript.annotations.ZenOperator; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * + * @author Stan + */ +@ZenClass("minecraft.data.IData") +public interface IData { + @ZenOperator(OperatorType.ADD) + public IData add(IData other); + + @ZenOperator(OperatorType.SUB) + public IData sub(IData other); + + @ZenOperator(OperatorType.MUL) + public IData mul(IData other); + + @ZenOperator(OperatorType.DIV) + public IData div(IData other); + + @ZenOperator(OperatorType.MOD) + public IData mod(IData other); + + @ZenOperator(OperatorType.AND) + public IData and(IData other); + + @ZenOperator(OperatorType.OR) + public IData or(IData other); + + @ZenOperator(OperatorType.XOR) + public IData xor(IData other); + + @ZenOperator(OperatorType.NEG) + public IData neg(); + + @ZenOperator(OperatorType.NOT) + public IData not(); + + @ZenCaster + public boolean asBool(); + + @ZenCaster + public byte asByte(); + + @ZenCaster + public short asShort(); + + @ZenCaster + public int asInt(); + + @ZenCaster + public long asLong(); + + @ZenCaster + public float asFloat(); + + @ZenCaster + public double asDouble(); + + @ZenCaster + public String asString(); + + @ZenCaster + public List asList(); + + @ZenCaster + public Map asMap(); + + @ZenCaster + public byte[] asByteArray(); + + @ZenCaster + public int[] asIntArray(); + + @ZenOperator(OperatorType.INDEXGET) + public IData getAt(int i); + + @ZenOperator(OperatorType.INDEXSET) + public void setAt(int i, IData value); + + @ZenMemberGetter + public IData memberGet(String name); + + @ZenMemberSetter + public void memberSet(String name, IData data); + + @ZenGetter + public int length(); + + @ZenOperator(OperatorType.CONTAINS) + public boolean contains(IData data); + + @ZenOperator(OperatorType.COMPARE) + public int compareTo(IData data); + + @ZenOperator(OperatorType.EQUALS) + public boolean equals(IData data); + + @ZenGetter + public IData immutable(); + + @ZenMethod + public IData update(IData data); + + public T convert(IDataConverter converter); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/IDataConverter.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/IDataConverter.java new file mode 100644 index 0000000..492dc22 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/data/IDataConverter.java @@ -0,0 +1,41 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.data; + +import java.util.List; +import java.util.Map; + +/** + * + * @author Stan + * @param output type + */ +public interface IDataConverter { + public T fromBool(boolean value); + + public T fromByte(byte value); + + public T fromShort(short value); + + public T fromInt(int value); + + public T fromLong(long value); + + public T fromFloat(float value); + + public T fromDouble(double value); + + public T fromString(String value); + + public T fromList(List values); + + public T fromMap(Map values); + + public T fromByteArray(byte[] value); + + public T fromIntArray(int[] value); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/Condition.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/Condition.java new file mode 100644 index 0000000..35e49ef --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/Condition.java @@ -0,0 +1,121 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.item; + +import minetweaker.minecraft.data.IData; +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenGetter; +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * Conditions can be used to set requirements for matching items. They can be + * used to require specific NBT tags or damage values, requiring changed items + * or as complement to damage-consuming or stack-consuming recipes. + * + * @author Stan Hebben + */ +@ZenClass("minecraft.item.Condition") +public class Condition { + /** + * Requires the item to be damaged. A damage value of 1 is enough. + * + * @return damaged condition + */ + @ZenGetter + public static IItemCondition damaged() { + return new IItemCondition() { + @Override + public boolean matches(IItemStack stack) { + return stack.getDamage() > 0; + } + }; + } + + /** + * Requires the item to be damaged with at least a specified amount. + * + * @param minDamage minimum damage + * @return damage condition + */ + @ZenMethod + public static IItemCondition damageAtLeast(final int minDamage) { + return new IItemCondition() { + @Override + public boolean matches(IItemStack stack) { + return stack.getDamage() >= minDamage; + } + }; + } + + /** + * Requires the item to be damaged with at most a specified amount. An + * undamaged item is accepted. + * + * @param maxDamage maximum damage + * @return damage condition + */ + @ZenMethod + public static IItemCondition damageAtMost(final int maxDamage) { + return new IItemCondition() { + @Override + public boolean matches(IItemStack stack) { + return stack.getDamage() <= maxDamage; + } + }; + } + + /** + * Requires the item damage value to be between two values. + * + * @param minDamage minimum damage + * @param maxDamage maximum damage + * @return damage condition + */ + @ZenMethod + public static IItemCondition damageBetween(final int minDamage, final int maxDamage) { + return new IItemCondition() { + @Override + public boolean matches(IItemStack stack) { + return stack.getDamage() >= minDamage && stack.getDamage() <= maxDamage; + } + }; + } + + /** + * Requires the item to contain at least the given tags. It is OK for the + * item to contain more data, as remaining data is ignored. + * + * @param data required data + * @return data condition + */ + @ZenMethod + public static IItemCondition data(final IData data) { + return new IItemCondition() { + @Override + public boolean matches(IItemStack stack) { + return stack.getTag() != null && stack.getTag().contains(data); + } + }; + } + + /** + * Requires the input stack to contain at least the given number of items. + * Used in combination with stack consuming recipes. + * + * @param amount required stack size + * @return stack size condition + */ + @ZenMethod + public static IItemCondition stack(final int amount) { + return new IItemCondition() { + @Override + public boolean matches(IItemStack stack) { + return stack.getAmount() >= amount; + } + }; + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IIngredient.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IIngredient.java new file mode 100644 index 0000000..bd4844e --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IIngredient.java @@ -0,0 +1,121 @@ +package minetweaker.minecraft.item; + +import java.util.List; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenGetter; +import stanhebben.zenscript.annotations.ZenMethod; +import stanhebben.zenscript.annotations.ZenOperator; + +/** + * Represents a recipe ingredient. An ingredient can be an item, an ore + * dictionary entry, an item stack, an ore dictionary entry multiplied with + * an amount, or other kinds of ingredients as offered by the API. + * + * There is no guarantee that every mod will respect the conditions or + * transformations, or that every mod will support every kind of ingredient. + * + * @author Stan Hebben + */ +@ZenClass("minecraft.item.IIngredient") +public interface IIngredient { + /** + * Gets the mark of the ingredient. Ingredients can be marked so they can + * be used in crafting functions. + * + * @return ingredient mark + */ + @ZenGetter("mark") + public String getMark(); + + /** + * Gets the amount. Should return -1 if no amount is available and 1 for + * a single item. Stacks return the stack size. + * + * @return stack size + */ + @ZenGetter("amount") + public int getAmount(); + + /** + * Gets all possible items for this ingredient. If there is no item list + * (for example, it is the <*> wildcard item) null should be returned. + * + * @return the items for this ingredient, or null + */ + @ZenGetter("items") + public List getItems(); + + /** + * Returns a new ingredient with the given stack size. + * + * @param amount new stack size + * @return modified ingredient + */ + @ZenOperator(OperatorType.MUL) + @ZenMethod + public IIngredient amount(int amount); + + /** + * Returns a new ingredient with the given transform added to it. + * + * @param transformer transformer to add + * @return modified ingredient + */ + @ZenMethod + public IIngredient transform(IItemTransformer transformer); + + /** + * Returns a new ingredient with the given condition added to it. + * + * @param condition condition to add + * @return modified ingredient + */ + @ZenMethod + public IIngredient only(IItemCondition condition); + + /** + * Returns a new ingredient marked with the given name. + * + * @param mark mark to apply + * @return modified ingredient + */ + @ZenMethod + public IIngredient marked(String mark); + + /** + * Checks if this ingredient matches the given item. + * + * @param item item to check + * @return true if the item matches + */ + @ZenMethod + public boolean matches(IItemStack item); + + /** + * Check if this ingredient contains all possible values for the given + * ingredient. + * + * @param ingredient ingredient to check + * @return true if the ingredient contains the given one + */ + @ZenOperator(OperatorType.CONTAINS) + public boolean contains(IIngredient ingredient); + + /** + * Applies transformations, if any, to the given item. + * + * @param item item to transform + * @return transformed item + */ + @ZenMethod + public IItemStack applyTransform(IItemStack item); + + /** + * Gets the internal item backing this ingredient. The value is + * implementation-dependent. + * + * @return internal item + */ + public Object getInternal(); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IItemCondition.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IItemCondition.java new file mode 100644 index 0000000..a1d4728 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IItemCondition.java @@ -0,0 +1,18 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.item; + +import stanhebben.zenscript.annotations.ZenClass; + +/** + * + * @author Stan + */ +@ZenClass("minecraft.item.IItemCondition") +public interface IItemCondition { + public boolean matches(IItemStack stack); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IItemDefinition.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IItemDefinition.java new file mode 100644 index 0000000..eece4ab --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IItemDefinition.java @@ -0,0 +1,15 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.item; + +/** + * + * @author Stan + */ +public interface IItemDefinition { + +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IItemStack.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IItemStack.java new file mode 100644 index 0000000..57c1332 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IItemStack.java @@ -0,0 +1,56 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.item; + +import minetweaker.minecraft.data.IData; +import minetweaker.minecraft.liquid.ILiquidStack; +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenGetter; +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * + * @author Stan + */ +@ZenClass("minecraft.item.IItemStack") +public interface IItemStack extends IIngredient { + @ZenGetter("definition") + public IItemDefinition getDefinition(); + + @ZenGetter("name") + public String getName(); + + @ZenGetter("displayName") + public String getDisplayName(); + + @ZenGetter("damage") + public int getDamage(); + + @ZenGetter("tag") + public IData getTag(); + + @ZenGetter("maxDamage") + public int getMaxDamage(); + + @ZenGetter("liquid") + public ILiquidStack getLiquid(); + + @ZenMethod + public IIngredient anyDamage(); + + @ZenMethod + public IItemStack withDamage(int damage); + + @ZenMethod + public IItemStack withAmount(int amount); + + @ZenMethod + public IItemStack withTag(IData tag); + + @ZenMethod + public IItemStack updateTag(IData tagUpdate); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IItemTransformer.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IItemTransformer.java new file mode 100644 index 0000000..8605f0f --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IItemTransformer.java @@ -0,0 +1,30 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.item; + +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * An item transformer modifies an item stack. Used to apply damage, grab multiple + * items, change NBT tags, replace the item with something else - or whatever + * else you'd want to happen to the input stack. + * + * @author Stan Hebben + */ +@ZenClass("minetweaker.IItemTransformer") +public interface IItemTransformer { + /** + * Transforms the specified item. May modify this item (and return it) + * or return an entirely new one. + * + * @param item input item + * @return output item + */ + @ZenMethod + public IItemStack transform(IItemStack item); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientAny.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientAny.java new file mode 100644 index 0000000..dbe5e26 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientAny.java @@ -0,0 +1,75 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.item; + +import java.util.List; + +/** + * + * @author Stan + */ +public class IngredientAny implements IIngredient { + public static IngredientAny INSTANCE = new IngredientAny(); + public static Object INTERNAL_ANY = null; // platforms supporting an "any" item should fill it here + + private IngredientAny() {} + + @Override + public String getMark() { + return null; + } + + @Override + public int getAmount() { + return -1; + } + + @Override + public List getItems() { + return null; + } + + @Override + public IIngredient amount(int amount) { + return new IngredientStack(this, amount); + } + + @Override + public IIngredient transform(IItemTransformer transformer) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IIngredient only(IItemCondition condition) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IIngredient marked(String mark) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public boolean matches(IItemStack item) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public boolean contains(IIngredient ingredient) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IItemStack applyTransform(IItemStack item) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Object getInternal() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientItem.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientItem.java new file mode 100644 index 0000000..6b1b3c0 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientItem.java @@ -0,0 +1,102 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.item; + +import java.util.Collections; +import java.util.List; +import minetweaker.minecraft.util.ArrayUtil; + +/** + * + * @author Stan + */ +public class IngredientItem implements IIngredient { + private final IItemStack item; + private final String mark; + private final IItemCondition[] conditions; + private final IItemTransformer[] transformers; + private final List items; + + public IngredientItem(IItemStack item, String mark, IItemCondition[] conditions, IItemTransformer[] transformers) { + this.item = item; + this.mark = mark; + this.conditions = conditions; + this.transformers = transformers; + + items = Collections.singletonList(item); + } + + @Override + public String getMark() { + return mark; + } + + @Override + public int getAmount() { + return item.getAmount(); + } + + @Override + public List getItems() { + return items; + } + + @Override + public IIngredient amount(int amount) { + return new IngredientStack(this, amount); + } + + @Override + public IIngredient transform(IItemTransformer transformer) { + return new IngredientItem(item, mark, conditions, ArrayUtil.append(transformers, transformer)); + } + + @Override + public IIngredient only(IItemCondition condition) { + return new IngredientItem(item, mark, ArrayUtil.append(conditions, condition), transformers); + } + + @Override + public IIngredient marked(String mark) { + return new IngredientItem(item, mark, conditions, transformers); + } + + @Override + public boolean matches(IItemStack item) { + if (!item.matches(item)) return false; + + for (IItemCondition condition : conditions) { + if (!condition.matches(item)) return false; + } + + return true; + } + + @Override + public boolean contains(IIngredient ingredient) { + List iitems = ingredient.getItems(); + for (IItemStack iitem : iitems) { + if (!matches(iitem)) return false; + } + + return true; + } + + @Override + public IItemStack applyTransform(IItemStack item) { + for (IItemTransformer transform : transformers) { + item = transform.transform(item); + } + + return item; + } + + @Override + public Object getInternal() { + return item.getInternal(); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientStack.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientStack.java new file mode 100644 index 0000000..f3b3731 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/IngredientStack.java @@ -0,0 +1,78 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.item; + +import java.util.List; + +/** + * + * @author Stan + */ +public class IngredientStack implements IIngredient { + private final IIngredient ingredient; + private final int amount; + + public IngredientStack(IIngredient ingredient, int amount) { + this.ingredient = ingredient; + this.amount = amount; + } + + @Override + public String getMark() { + return null; + } + + @Override + public int getAmount() { + return amount; + } + + @Override + public List getItems() { + return ingredient.getItems(); + } + + @Override + public IIngredient amount(int amount) { + return new IngredientStack(ingredient, amount); + } + + @Override + public IIngredient transform(IItemTransformer transformer) { + return new IngredientStack(ingredient.transform(transformer), amount); + } + + @Override + public IIngredient only(IItemCondition condition) { + return new IngredientStack(ingredient.only(condition), amount); + } + + @Override + public IIngredient marked(String mark) { + return new IngredientStack(ingredient.marked(mark), amount); + } + + @Override + public boolean matches(IItemStack item) { + return item.getAmount() == amount && ingredient.matches(item); + } + + @Override + public boolean contains(IIngredient ingredient) { + return this.ingredient.contains(ingredient); + } + + @Override + public IItemStack applyTransform(IItemStack item) { + return ingredient.applyTransform(item); + } + + @Override + public Object getInternal() { + return null; + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/Transform.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/Transform.java new file mode 100644 index 0000000..68ec2ff --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/item/Transform.java @@ -0,0 +1,114 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.item; + +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * + * @author Stan + */ +@ZenClass("minecraft.item.Transform") +public class Transform { + /** + * Makes the item reusable. Prevents consumption of the item upon crafting. + * + * @return reuse transformer + */ + @ZenMethod + public IItemTransformer reuse() { + return new IItemTransformer() { + @Override + public IItemStack transform(IItemStack item) { + return item.withAmount(item.getAmount() + 1); + } + }; + } + + /** + * Damages the item. Also makes the item reusable. Will damage the item + * for 1 point upon crafting and consume it when broken. + * + * @return damage transformer + */ + @ZenMethod + public IItemTransformer damage() { + return new IItemTransformer() { + @Override + public IItemStack transform(IItemStack item) { + int newDamage = item.getDamage() + 1; + if (newDamage >= item.getMaxDamage()) { + return item.withAmount(item.getAmount() - 1).withDamage(0); + } else { + return item.withDamage(newDamage); + } + } + }; + } + + /** + * Damages the item for a specific amount. Also makes the item reusable. + * Upon reaching maximum damage, the item will be consumed. Take care to + * set the proper condition such that an almost-broken item becomes + * invalid for crafting. + * + * @param damage damage to be applied + * @return damage transformer + */ + @ZenMethod + public IItemTransformer damage(final int damage) { + return new IItemTransformer() { + @Override + public IItemStack transform(IItemStack item) { + int newDamage = item.getDamage() + damage; + if (newDamage >= item.getMaxDamage()) { + return item.withAmount(item.getAmount() - 1).withDamage(newDamage - item.getMaxDamage()); + } else { + return item.withDamage(newDamage); + } + } + }; + } + + /** + * Causes the item to be replaced upon crafting. Can be used, for instance, + * to return empty bottles or buckets. + * + * @param withItem replacement item + * @return replacement transformer + */ + @ZenMethod + public IItemTransformer replaceWith(IItemStack withItem) { + final IItemStack result = withItem.withAmount(withItem.getAmount() + 1); + + return new IItemTransformer() { + @Override + public IItemStack transform(IItemStack item) { + return result; + } + }; + } + + /** + * Causes multiple items to be consumed. Take care to set a condition for + * a minimum stack size too, as otherwise smaller stacks would still be + * accepted for input. + * + * @param amount consumption amount + * @return consuming transformer + */ + @ZenMethod + public IItemTransformer consume(final int amount) { + return new IItemTransformer() { + @Override + public IItemStack transform(IItemStack item) { + return item.withAmount(Math.max(item.getAmount() - amount, 0) + 1); + } + }; + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/liquid/ILiquidDefinition.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/liquid/ILiquidDefinition.java new file mode 100644 index 0000000..dac6496 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/liquid/ILiquidDefinition.java @@ -0,0 +1,53 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.liquid; + +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenGetter; +import stanhebben.zenscript.annotations.ZenOperator; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.annotations.ZenSetter; + +/** + * + * @author Stanneke + */ +@ZenClass("minecraft.liquid.ILiquidDefinition") +public interface ILiquidDefinition { + @ZenOperator(OperatorType.MUL) + public ILiquidStack asStack(int millibuckets); + + @ZenGetter("luminosity") + public int getLuminosity(); + + @ZenSetter("luminosity") + public void setLuminosity(int value); + + @ZenGetter("density") + public int getDensity(); + + @ZenSetter("density") + public void setDensity(int density); + + @ZenGetter("temperature") + public int getTemperature(); + + @ZenSetter("temperature") + public void setTemperature(int temperature); + + @ZenGetter("viscosity") + public int getViscosity(); + + @ZenSetter("viscosity") + public void setViscosity(int viscosity); + + @ZenGetter("gaseous") + public boolean isGaseous(); + + @ZenSetter("gaseous") + public void setGaseous(boolean gaseous); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/liquid/ILiquidStack.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/liquid/ILiquidStack.java new file mode 100644 index 0000000..2aabf47 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/liquid/ILiquidStack.java @@ -0,0 +1,51 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.liquid; + +import minetweaker.minecraft.data.IData; +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenGetter; +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * + * @author Stanneke + */ +@ZenClass("minecraft.liquid.ILiquidStack") +public interface ILiquidStack { + @ZenGetter("definition") + public ILiquidDefinition getDefinition(); + + @ZenGetter("amount") + public int getAmount(); + + @ZenGetter("luminosity") + public int getLuminosity(); + + @ZenGetter("density") + public int getDensity(); + + @ZenGetter("temperature") + public int getTemperature(); + + @ZenGetter("viscosity") + public int getViscosity(); + + @ZenGetter("gaseous") + public boolean isGaseous(); + + @ZenGetter("tag") + public IData getTag(); + + @ZenMethod + public ILiquidStack withTag(IData data); + + @ZenMethod + public ILiquidStack withAmount(int amount); + + public Object getInternal(); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/oredict/IOreDict.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/oredict/IOreDict.java new file mode 100644 index 0000000..1d69edf --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/oredict/IOreDict.java @@ -0,0 +1,20 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.oredict; + +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenMemberGetter; + +/** + * + * @author Stan + */ +@ZenClass("minecraft.oredict.IOreDict") +public interface IOreDict { + @ZenMemberGetter + public IOreDictEntry get(String name); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/oredict/IOreDictEntry.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/oredict/IOreDictEntry.java new file mode 100644 index 0000000..6518341 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/oredict/IOreDictEntry.java @@ -0,0 +1,34 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.oredict; + +import minetweaker.minecraft.item.IIngredient; +import minetweaker.minecraft.item.IItemStack; +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * + * @author Stan + */ +@ZenClass("minecraft.oreDict.IOreDictEntry") +public interface IOreDictEntry extends IIngredient { + @ZenMethod + public void add(IItemStack item); + + @ZenMethod + public void addAll(IOreDictEntry entry); + + @ZenMethod + public void remove(IItemStack item); + + @ZenMethod + public boolean contains(IItemStack item); + + @ZenMethod + public void mirror(IOreDictEntry other); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/oredict/IngredientOreDict.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/oredict/IngredientOreDict.java new file mode 100644 index 0000000..acda168 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/oredict/IngredientOreDict.java @@ -0,0 +1,102 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.oredict; + +import java.util.List; +import minetweaker.minecraft.item.IIngredient; +import minetweaker.minecraft.item.IItemCondition; +import minetweaker.minecraft.item.IItemStack; +import minetweaker.minecraft.item.IItemTransformer; +import minetweaker.minecraft.item.IngredientStack; +import minetweaker.minecraft.util.ArrayUtil; + +/** + * + * @author Stan Hebben + */ +public class IngredientOreDict implements IIngredient { + private final IOreDictEntry entry; + private final String mark; + private final IItemCondition[] conditions; + private final IItemTransformer[] transformers; + + public IngredientOreDict( + IOreDictEntry entry, + String mark, + IItemCondition[] conditions, + IItemTransformer[] transformers) { + this.entry = entry; + this.mark = mark; + this.conditions = conditions; + this.transformers = transformers; + } + + @Override + public String getMark() { + return mark; + } + + @Override + public int getAmount() { + return 1; + } + + @Override + public List getItems() { + return entry.getItems(); + } + + @Override + public IIngredient amount(int amount) { + return new IngredientStack(this, amount); + } + + @Override + public IIngredient transform(IItemTransformer transformer) { + return new IngredientOreDict(entry, mark, conditions, ArrayUtil.append(transformers, transformer)); + } + + @Override + public IIngredient only(IItemCondition condition) { + return new IngredientOreDict(entry, mark, ArrayUtil.append(conditions, condition), transformers); + } + + @Override + public IIngredient marked(String mark) { + return new IngredientOreDict(entry, mark, conditions, transformers); + } + + @Override + public boolean matches(IItemStack item) { + if (!entry.matches(item)) return false; + + for (IItemCondition condition : conditions) { + if (!condition.matches(item)) return false; + } + + return true; + } + + @Override + public boolean contains(IIngredient ingredient) { + return entry.contains(ingredient); + } + + @Override + public IItemStack applyTransform(IItemStack item) { + for (IItemTransformer transformer : transformers) { + item = transformer.transform(item); + } + + return item; + } + + @Override + public Object getInternal() { + return entry.getInternal(); + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/ICraftingInventory.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/ICraftingInventory.java new file mode 100644 index 0000000..b5338df --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/ICraftingInventory.java @@ -0,0 +1,29 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.recipes; + +import minetweaker.minecraft.item.IItemStack; + +/** + * + * @author Stan + */ +public interface ICraftingInventory { + public int getSize(); + + public int getWidth(); + + public int getHeight(); + + public IItemStack getStack(int i); + + public IItemStack getStack(int x, int y); + + public void setStack(int x, int y, IItemStack stack); + + public void setStack(int i, IItemStack stack); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/IFurnaceManager.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/IFurnaceManager.java new file mode 100644 index 0000000..4e5f314 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/IFurnaceManager.java @@ -0,0 +1,30 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.recipes; + +import minetweaker.minecraft.item.IIngredient; +import minetweaker.minecraft.item.IItemStack; +import stanhebben.zenscript.annotations.Optional; +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * + * @author Stan + */ +public interface IFurnaceManager { + @ZenMethod + public void remove(IIngredient ingredient); + + @ZenMethod + public void addRecipe(IIngredient output, IIngredient input, @Optional double xp); + + @ZenMethod + public void setFuel(IIngredient item, int fuel); + + @ZenMethod + public int getFuel(IItemStack item); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/IRecipeFunction.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/IRecipeFunction.java new file mode 100644 index 0000000..8e475c6 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/IRecipeFunction.java @@ -0,0 +1,18 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.recipes; + +import java.util.Map; +import minetweaker.minecraft.item.IItemStack; + +/** + * + * @author Stan + */ +public interface IRecipeFunction { + public IItemStack process(IItemStack output, Map inputs); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/IRecipeManager.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/IRecipeManager.java new file mode 100644 index 0000000..01f9f7f --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/IRecipeManager.java @@ -0,0 +1,89 @@ +package minetweaker.minecraft.recipes; + +import minetweaker.minecraft.item.IIngredient; +import minetweaker.minecraft.item.IItemStack; +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.Optional; +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * The RecipeManager adds and removes crafting recipes. The IRecipeManager + * instance is available throug the recipes global variable. + * + * @author Stan Hebben + */ +@ZenClass("minetweaker.recipes.IRecipeManager") +public interface IRecipeManager { + /** + * Removes all crafting recipes crafting the specified item. + * + * @param output recipe output pattern + * @return number of removed recipes + */ + @ZenMethod + public int remove(IIngredient output); + + /** + * Adds a shaped recipe. + * + * @param output recipe output + * @param ingredients recipe ingredients + * @param function recipe function (optional) + * @param mirrored indicates if the mirrored recipe should be valid too (optional) + */ + @ZenMethod + public void addShaped( + IItemStack output, + IIngredient[][] ingredients, + @Optional IRecipeFunction function, + @Optional boolean mirrored); + + /** + * Adds a shapeless recipe. + * + * @param output recipe output + * @param ingredients recipe ingredients + * @param function recipe function (optional) + */ + @ZenMethod + public void addShapeless( + IItemStack output, + IIngredient[] ingredients, + @Optional IRecipeFunction function); + + /** + * Removes shaped recipes. + * + * @param output recipe output + * @param ingredients recipe ingredients + * @return number of removed recipes + */ + @ZenMethod + public int removeShaped( + IIngredient output, + @Optional IIngredient[][] ingredients); + + /** + * Removes shapeless recipes. + * + * @param output recipe output + * @param ingredients recipe ingredients + * @param wildcard if the recipe may contain other ingredients too, besides the ones specified + * @return number of removed recipes + */ + @ZenMethod + public int removeShapeless( + IIngredient output, + @Optional IIngredient[] ingredients, + @Optional boolean wildcard); + + /** + * Performs a crafting with the specified ingredients. Returns null if no + * crafting recipe exists. + * + * @param contents crafting inventory contents + * @return crafting result, or null + */ + @ZenMethod + public IItemStack craft(IItemStack[][] contents); +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/ShapedRecipe.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/ShapedRecipe.java new file mode 100644 index 0000000..5fbdd33 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/ShapedRecipe.java @@ -0,0 +1,217 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.recipes; + +import java.util.HashMap; +import java.util.Map; +import minetweaker.minecraft.item.IIngredient; +import minetweaker.minecraft.item.IItemStack; + +/** + * + * @author Stan + */ +public class ShapedRecipe { + private final int width; + private final int height; + private final byte[] posx; + private final byte[] posy; + private final boolean mirrored; + private final IRecipeFunction function; + + private final IItemStack output; + private final IIngredient[] ingredients; + + public ShapedRecipe(IItemStack output, IIngredient[][] ingredients, IRecipeFunction function, boolean mirrored) { + int numIngredients = 0; + for (IIngredient[] row : ingredients) { + for (IIngredient ingredient : row) { + if (ingredient != null) { + numIngredients++; + } + } + } + + this.posx = new byte[numIngredients]; + this.posy = new byte[numIngredients]; + this.output = output; + this.ingredients = new IIngredient[numIngredients]; + this.function = function; + + int width1 = 0; + int height1 = ingredients.length; + + int ix = 0; + for (int j = 0; j < ingredients.length; j++) { + IIngredient[] row = ingredients[j]; + width1 = Math.max(width1, row.length); + + for (int i = 0; i < row.length; i++) { + if (row[i] != null) { + this.posx[ix] = (byte) i; + this.posy[ix] = (byte) j; + this.ingredients[ix] = row[i]; + ix++; + } + } + } + + this.width = width1; + this.height = height1; + this.mirrored = mirrored; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public boolean isMirrored() { + return mirrored; + } + + public IIngredient[] getIngredients() { + return ingredients; + } + + public byte[] getIngredientsX() { + return posx; + } + + public byte[] getIngredientsY() { + return posy; + } + + public IItemStack getOutput() { + return output; + } + + public boolean matches(ICraftingInventory inventory) { + int numItems = 0; + for (int i = 0; i < inventory.getSize(); i++) { + if (inventory.getStack(i) != null) { + numItems++; + } + } + + if (numItems != ingredients.length) { + return false; + } + + for (int i = 0; i < inventory.getWidth() - width; i++) { + out: for (int j = 0; j < inventory.getHeight() - height; j++) { + for (int k = 0; k < ingredients.length; k++) { + IItemStack item = inventory.getStack(posx[k] + i, posy[k] + j); + if (item == null) continue out; + + if (!ingredients[k].matches(item)) continue out; + } + + return true; + } + } + + if (mirrored) { + for (int i = 0; i < inventory.getWidth() - width; i++) { + out: for (int j = 0; j < inventory.getHeight() - height; j++) { + for (int k = 0; k < ingredients.length; k++) { + IItemStack item = inventory.getStack(inventory.getWidth() - (posx[k] + i), posy[k] + j); + if (item == null) continue out; + + if (!ingredients[k].matches(item)) continue out; + } + + return true; + } + } + } + + return false; + } + + public IItemStack getCraftingResult(ICraftingInventory inventory) { + IItemStack[] stacks = new IItemStack[ingredients.length]; + + for (int i = 0; i < inventory.getWidth() - width; i++) { + out: for (int j = 0; j < inventory.getHeight() - height; j++) { + for (int k = 0; k < ingredients.length; k++) { + IItemStack item = inventory.getStack(posx[k] + i, posy[k] + j); + if (item == null) continue out; + + if (!ingredients[k].matches(item)) continue out; + stacks[k] = item; + } + + return doRecipe(inventory, stacks, i, j, output, false); + } + } + + if (mirrored) { + for (int i = 0; i < inventory.getWidth() - width; i++) { + out: for (int j = 0; j < inventory.getHeight() - height; j++) { + for (int k = 0; k < ingredients.length; k++) { + IItemStack item = inventory.getStack(inventory.getWidth() - (posx[k] + i), posy[k] + j); + if (item == null) continue out; + + if (!ingredients[k].matches(item)) continue out; + stacks[k] = item; + } + + return doRecipe(inventory, stacks, i, j, output, true); + } + } + } + + return null; + } + + private IItemStack doRecipe( + ICraftingInventory inventory, + IItemStack[] stacks, + int offx, int offy, + IItemStack output, + boolean mirrored) { + // determine output and apply transformations + + if (function != null) { + Map tagged = new HashMap(); + for (int k = 0; k < ingredients.length; k++) { + if (ingredients[k].getMark() != null) { + tagged.put(ingredients[k].getMark(), stacks[k]); + } + } + + output = function.process(output, tagged); + } + + if (output == null) { + return null; + } + + for (int i = 0; i < ingredients.length; i++) { + IItemStack transformed = ingredients[i].applyTransform(stacks[i]); + if (transformed != stacks[i]) { + if (mirrored) { + inventory.setStack( + inventory.getWidth() - (offx + posx[i]), + offy + posy[i], + transformed); + } else { + inventory.setStack( + offx + posx[i], + offy + posy[i], + transformed); + } + } + } + + return output; + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/ShapelessRecipe.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/ShapelessRecipe.java new file mode 100644 index 0000000..74007b9 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/recipes/ShapelessRecipe.java @@ -0,0 +1,128 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.recipes; + +import java.util.HashMap; +import java.util.Map; +import minetweaker.minecraft.item.IIngredient; +import minetweaker.minecraft.item.IItemStack; + +/** + * + * @author Stan + */ +public class ShapelessRecipe { + private final IItemStack output; + private final IRecipeFunction function; + private final IIngredient[] ingredients; + + public ShapelessRecipe(IItemStack output, IIngredient[] ingredients, IRecipeFunction function) { + this.output = output; + this.function = function; + this.ingredients = ingredients; + } + + public int getSize() { + return ingredients.length; + } + + public IIngredient[] getIngredients() { + return ingredients; + } + + public IItemStack getOutput() { + return output; + } + + public boolean matches(ICraftingInventory inventory) { + return matchShapeless(ingredients, inventory) != null; + } + + public IItemStack getCraftingResult(ICraftingInventory inventory) { + RecipeMatching matching = matchShapeless(ingredients, inventory); + + IItemStack actualOutput = output; + if (function != null) { + Map map = new HashMap(); + for (int i = 0; i < ingredients.length; i++) { + if (ingredients[i].getMark() != null) { + map.put(ingredients[i].getMark(), matching.inputs[i]); + } + } + + actualOutput = function.process(actualOutput, map); + } + + if (actualOutput == null) { + return null; + } + + for (int i = 0; i < ingredients.length; i++) { + IIngredient ingredient = ingredients[i]; + IItemStack transformed = ingredient.applyTransform(matching.inputs[i]); + if (transformed != matching.inputs[i]) { + inventory.setStack(matching.indices[i], transformed); + } + } + + return actualOutput; + } + + /** + * Reorders ingredients to match the given shapeless recipe. + * + * @param recipe shapeless recipe + * @param ingredients crafting input + * @return reordered inputs, or null if no match was found + */ + private static RecipeMatching matchShapeless( + IIngredient[] recipe, + ICraftingInventory ingredients) { + int numItems = 0; + for (int i = 0; i < ingredients.getSize(); i++) { + if (ingredients.getStack(i) != null) { + numItems++; + } + } + + if (numItems != recipe.length) { + return null; + } + + IItemStack[] matched = new IItemStack[recipe.length]; + int[] indices = new int[recipe.length]; + + outer: for (int i = 0; i < ingredients.getSize(); i++) { + IItemStack ingredient = ingredients.getStack(i); + if (ingredient == null) continue; + + for (int j = 0; j < recipe.length; j++) { + if (matched[j] != null) continue; + + if (recipe[j].matches(ingredient)) { + matched[j] = ingredient; + indices[j] = i; + continue outer; + } + } + + return null; + } + + return new RecipeMatching(matched, indices); + } + + private static class RecipeMatching { + public final IItemStack[] inputs; + public final int[] indices; + + public RecipeMatching(IItemStack[] inputs, int[] indices) { + this.inputs = inputs; + this.indices = indices; + } + } +} diff --git a/MineTweaker3-API/src/main/java/minetweaker/minecraft/util/ArrayUtil.java b/MineTweaker3-API/src/main/java/minetweaker/minecraft/util/ArrayUtil.java new file mode 100644 index 0000000..b272867 --- /dev/null +++ b/MineTweaker3-API/src/main/java/minetweaker/minecraft/util/ArrayUtil.java @@ -0,0 +1,32 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.minecraft.util; + +import java.util.Arrays; +import minetweaker.minecraft.item.IItemCondition; +import minetweaker.minecraft.item.IItemTransformer; + +/** + * + * @author Stan + */ +public class ArrayUtil { + public static final IItemCondition[] EMPTY_CONDITIONS = new IItemCondition[0]; + public static final IItemTransformer[] EMPTY_TRANSFORMERS = new IItemTransformer[0]; + + public static IItemCondition[] append(IItemCondition[] values, IItemCondition value) { + IItemCondition[] result = Arrays.copyOf(values, values.length + 1); + result[values.length] = value; + return result; + } + + public static IItemTransformer[] append(IItemTransformer[] values, IItemTransformer value) { + IItemTransformer[] result = Arrays.copyOf(values, values.length + 1); + result[values.length] = value; + return result; + } +} diff --git a/MineTweaker3-MC17-Main/build.gradle b/MineTweaker3-MC17-Main/build.gradle new file mode 100644 index 0000000..e3c60b8 --- /dev/null +++ b/MineTweaker3-MC17-Main/build.gradle @@ -0,0 +1,28 @@ +buildscript { + repositories { + mavenCentral() + maven { + name = "forge" + url = "http://files.minecraftforge.net/maven" + } + maven { + name = "sonatype" + url = "https://oss.sonatype.org/content/repositories/snapshots/" + } + } + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT' + } +} + +apply plugin: 'forge' +minecraft{version = "1.7.2-10.12.1.1112"} +version = "1.0" +archivesBaseName = project.projectDir.name + +dependencies { + dependencies { + compile project(':ZenScript') + compile project(':MineTweaker3-API') + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerCommand.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerCommand.java new file mode 100644 index 0000000..23c6b28 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerCommand.java @@ -0,0 +1,264 @@ +package minetweaker.mc172; + +import java.util.Arrays; +import java.util.List; + +import net.minecraft.command.ICommand; +import net.minecraft.command.ICommandSender; +import net.minecraft.util.ChatComponentText; +import stanhebben.zenscript.util.StringUtil; + +/** + * Implements the /minetweaker command set. + * + * @author Stan Hebben + */ +public class MineTweakerCommand implements ICommand { + // ########################### + // ## Public static methods ## + // ########################### + + public static void sendChatMessage(ICommandSender sender, String message) { + sender.addChatMessage(new ChatComponentText(message)); + } + + private static final List aliases = Arrays.asList("mt"); + + // ####################################### + // ## ICommand interface implementation ## + // ####################################### + + @Override + public int compareTo(Object arg0) { + return 0; + } + + @Override + public String getCommandName() { + return "minetweaker"; + } + + @Override + public String getCommandUsage(ICommandSender icommandsender) { + return StringUtil.join(new String[] { + "", + "minetweaker name [:]", + " Prints the unlocalized name of the specified item," + + " can be used as item or block name", + "minetweaker liquid [:]", + " Prints the unlocalized liquid name in the specified container," + + " con be used as liquid name", + "minetweaker oredict", + " Prints a list of all ore dictionary entries", + "minetweaker oredict [:]", + " Prints the ore dictionary name(s) for the specified item", + "minetweaker oredict ", + " Prints all items for the specified ore dictionary name", + "minetweaker recipes [:]", + " Prints all recipes for the specified item", + "minetweaker fuel [:]", + " Retrieves the fuel value for the specified item", + "minetweaker reload", + " Reloads the server-side script (experimental)", + "minetweaker logdamage=on", + " Enables damage logging", + "minetweaker logdamage=off", + " Disables damage logging", + "minetweaker reload", + " Reloads the server script" + }, "\n"); + } + + @Override + public List getCommandAliases() { + return aliases; + } + + @Override + public void processCommand(ICommandSender icommandsender, String[] arguments) { + if (arguments.length == 0) { + sendChatMessage(icommandsender, "Please provide a command. Use /help minetweaker for more info."); + return; + } + + if (arguments[0].equals("reload")) { + MineTweakerMod.instance.reloadScripts(); + } + + /*if (arguments[0].equals("name")) { + if (arguments.length < 2) { + sendChatMessage(icommandsender, "Please provide an item id"); + } else { + try { + ITweakerItem item = TweakerItem.parse(arguments[1]); + sendChatMessage(icommandsender, "Name for " + item.toIdString() + ": " + MineTweakerUtil.formatItemName(item.getName())); + } catch (TweakerExecuteException ex) { + sendChatMessage(icommandsender, ex.getMessage()); + } catch (TweakerException ex) { + sendChatMessage(icommandsender, ex.getExplanation()); + } + } + } else if (arguments[0].equals("liquid")) { + if (arguments.length < 2) { + sendChatMessage(icommandsender, "Please provide a liquid container item id"); + } else { + try { + TweakerItem item = TweakerItem.parse(arguments[1]); + //#ifdef MC152 + //+if (LiquidContainerRegistry.isLiquid(item.make(1))) { + //+sendChatMessage(icommandsender, "Liquid name: " + item.getName()); + //+} else if (LiquidContainerRegistry.isFilledContainer(item.make(1))) { + //+sendChatMessage(icommandsender, "Liquid name: " + LiquidContainerRegistry.getLiquidForFilledItem(item.make(1)).asItemStack().getItemName()); + //+} else { + //+sendChatMessage(icommandsender, "This item is not a liquid or liquid container"); + //+} + //#else + if (item.getItemId() == 9) { + sendChatMessage(icommandsender, "Liquid name: fluid.tile.water"); + } else if (item.getItemId() == 11) { + sendChatMessage(icommandsender, "Liquid name: fluid.tile.lava"); + } else if (Block.blocksList[item.getItemId()] != null && Block.blocksList[item.getItemId()] instanceof IFluidBlock) { + sendChatMessage(icommandsender, "Liquid name: " + ((IFluidBlock)(Block.blocksList[item.getItemId()])).getFluid().getUnlocalizedName()); + } else if (FluidContainerRegistry.isFilledContainer(item.make(1))) { + sendChatMessage(icommandsender, "Liquid name: " + FluidContainerRegistry.getFluidForFilledItem(item.make(1)).getFluid().getUnlocalizedName()); + } else { + sendChatMessage(icommandsender, "This item is not a liquid or liquid container"); + } + //#endif + } catch (TweakerExecuteException ex) { + sendChatMessage(icommandsender, ex.getMessage()); + } catch (TweakerException ex) { + sendChatMessage(icommandsender, ex.getExplanation()); + } + } + } else if (arguments[0].equals("oredict")) { + if (arguments.length == 1) { + String[] names = OreDictionary.getOreNames(); + Arrays.sort(names); + for (String name : names) { + sendChatMessage(icommandsender, "[" + name + "]"); + ArrayList entries = OreDictionary.getOres(name); + for (ItemStack stack : entries) { + //#ifdef MC152 + //+sendChatMessage(icommandsender, + //+" " + MineTweakerUtil.getItemString(stack) + " - " + + //+MineTweakerUtil.formatItemName(stack.getItemDamage() == OreDictionary.WILDCARD_VALUE ? stack.getItem().getUnlocalizedName() : stack.getItemName())); + //#else + sendChatMessage(icommandsender, + " " + MineTweakerUtil.getItemString(stack) + " - " + + MineTweakerUtil.formatItemName(stack.getItemDamage() == OreDictionary.WILDCARD_VALUE ? stack.getItem().getUnlocalizedName() : stack.getUnlocalizedName())); + //#endif + } + } + } else { + try { + if (Character.isDigit(arguments[1].charAt(0))) { + TweakerItemPattern item = TweakerItem.parse(arguments[1]).asItemPattern(); + for (String name : OreDictionary.getOreNames()) { + for (ItemStack stack : OreDictionary.getOres(name)) { + if (item.matches(stack)) { + sendChatMessage(icommandsender, " " + name); + break; + } + } + } + } else { + for (ItemStack stack : OreDictionary.getOres(arguments[1])) { + //#ifdef MC152 + //+sendChatMessage(icommandsender, + //+" " + MineTweakerUtil.getItemString(stack) + " - " + + //+MineTweakerUtil.formatItemName(stack.getItemDamage() == OreDictionary.WILDCARD_VALUE ? stack.getItem().getUnlocalizedName() : stack.getItemName())); + //#else + sendChatMessage(icommandsender, + " " + MineTweakerUtil.getItemString(stack) + " - " + + MineTweakerUtil.formatItemName(stack.getItemDamage() == OreDictionary.WILDCARD_VALUE ? stack.getItem().getUnlocalizedName() : stack.getUnlocalizedName())); + //#endif + } + } + } catch (TweakerExecuteException ex) { + sendChatMessage(icommandsender, "Invalid item id or ore dictionary entry"); + } catch (TweakerException ex) { + sendChatMessage(icommandsender, "Invalid item id or ore dictionary entry"); + } + } + } else if (arguments[0].equals("recipes")) { + if (arguments.length < 2) { + sendChatMessage(icommandsender, "Please provide an item id"); + return; + } + try { + TweakerItem item = TweakerItem.parse(arguments[1]); + sendChatMessage(icommandsender, "Recipes for item " + item.toIdString()); + + TweakerItemPattern pattern = item.asItemPattern(); + + @SuppressWarnings("unchecked") + List recipes = CraftingManager.getInstance().getRecipeList(); + + for (IRecipe recipe : recipes) { + if (pattern.matches(recipe.getRecipeOutput())) { + sendChatMessage(icommandsender, " " + MineTweakerUtil.getRecipeString(recipe)); + } + } + } catch (TweakerExecuteException ex) { + sendChatMessage(icommandsender, "Invalid item id"); + } catch (TweakerException ex) { + sendChatMessage(icommandsender, "Invalid item id"); + } + } else if (arguments[0].equals("fuel")) { + if (arguments.length < 2) { + sendChatMessage(icommandsender, "Please provide an item id"); + return; + } + try { + TweakerItem item = TweakerItem.parse(arguments[1]); + ItemStack stack = item.make(1); + int value = TileEntityFurnace.getItemBurnTime(stack); + int regValue = GameRegistry.getFuelValue(stack); + + sendChatMessage(icommandsender, "Fuel for " + item.toIdString() + " = " + value); + if (value != regValue) { + sendChatMessage(icommandsender, "The value is hardcoded and cannot be altered."); + } + } catch (TweakerExecuteException ex) { + sendChatMessage(icommandsender, "Invalid item id"); + } catch (TweakerException ex) { + sendChatMessage(icommandsender, "Invalid item id"); + } + } else if (arguments[0].equals("reload")) { + if (!MineTweaker.instance.canRollback()) { + sendChatMessage(icommandsender, "Previous server script contained permanent actions, cannot reload. A restart is required to reload scripts."); + } else { + MineTweaker.instance.reloadScripts(); + sendChatMessage(icommandsender, "Script reloaded, updating all players"); + } + } else if (arguments[0].equals("logdamage=on")) { + DamageTweaker.logDamage = true; + sendChatMessage(icommandsender, "Damage logging turned on"); + sendChatMessage(icommandsender, "Info: logging is sent to the development console"); + } else if (arguments[0].equals("logdamage=off")) { + DamageTweaker.logDamage = false; + sendChatMessage(icommandsender, "Damage logging turned off"); + } else { + sendChatMessage(icommandsender, "Unrecognized minetweaker command"); + }*/ + + //MineCraftCompat.sendChatMessage(icommandsender, "[" + Arrays2.join(arguments, ", ") + "]"); + } + + @Override + public boolean canCommandSenderUseCommand(ICommandSender icommandsender) { + return true; + } + + @Override + public List addTabCompletionOptions(ICommandSender icommandsender, + String[] astring) { + return null; + } + + @Override + public boolean isUsernameIndex(String[] astring, int i) { + return false; + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerMod.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerMod.java new file mode 100644 index 0000000..2e9200f --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerMod.java @@ -0,0 +1,35 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172; + +import cpw.mods.fml.common.Mod; +import minetweaker.MineTweakerAPI; +import minetweaker.mc172.data.NBTConverter; +import minetweaker.mc172.oredict.OreDict; +import minetweaker.mc172.recipes.MTRecipeManager; + +/** + * + * @author Stanneke + */ +@Mod(modid = MineTweakerMod.MODID, version = MineTweakerMod.MCVERSION + "-3.0.0") +public class MineTweakerMod { + public static final String MODID = "MineTweaker"; + public static final String MCVERSION = "1.7.2"; + + @Mod.Instance(MODID) + public static MineTweakerMod instance; + + public MineTweakerMod() { + MineTweakerAPI.oreDict = new OreDict(); + MineTweakerAPI.recipes = new MTRecipeManager(); + } + + public void reloadScripts() { + // TODO: implements + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerRegistry.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerRegistry.java new file mode 100644 index 0000000..e366917 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/MineTweakerRegistry.java @@ -0,0 +1,19 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172; + +import java.util.List; + +/** + * + * @author Stan + */ +public class MineTweakerRegistry { + public static void getClasses(List classes) { + // method stub, actual content is generated by build process + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/data/NBTConverter.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/data/NBTConverter.java new file mode 100644 index 0000000..26564ab --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/data/NBTConverter.java @@ -0,0 +1,164 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.data; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import minetweaker.mc172.util.MineTweakerHacks; +import minetweaker.minecraft.data.DataByte; +import minetweaker.minecraft.data.DataByteArray; +import minetweaker.minecraft.data.DataDouble; +import minetweaker.minecraft.data.DataFloat; +import minetweaker.minecraft.data.DataInt; +import minetweaker.minecraft.data.DataIntArray; +import minetweaker.minecraft.data.DataList; +import minetweaker.minecraft.data.DataLong; +import minetweaker.minecraft.data.DataMap; +import minetweaker.minecraft.data.DataShort; +import minetweaker.minecraft.data.DataString; +import minetweaker.minecraft.data.IData; +import minetweaker.minecraft.data.IDataConverter; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagByte; +import net.minecraft.nbt.NBTTagByteArray; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagDouble; +import net.minecraft.nbt.NBTTagFloat; +import net.minecraft.nbt.NBTTagInt; +import net.minecraft.nbt.NBTTagIntArray; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.nbt.NBTTagLong; +import net.minecraft.nbt.NBTTagShort; +import net.minecraft.nbt.NBTTagString; + +/** + * + * @author Stan + */ +public class NBTConverter implements IDataConverter { + private static final NBTConverter INSTANCE = new NBTConverter(); + + public static NBTBase from(IData data) { + return data.convert(INSTANCE); + } + + public static IData from(NBTBase nbt, boolean immutable) { + if (nbt == null) return null; + + switch (nbt.getId()) { + case 1: // byte + return new DataByte(((NBTBase.NBTPrimitive) nbt).func_150290_f()); + case 2: // short + return new DataShort(((NBTBase.NBTPrimitive) nbt).func_150289_e()); + case 3: // int + return new DataInt(((NBTBase.NBTPrimitive) nbt).func_150287_d()); + case 4: // long + return new DataLong(((NBTBase.NBTPrimitive) nbt).func_150291_c()); + case 5: // float + return new DataFloat(((NBTBase.NBTPrimitive) nbt).func_150288_h()); + case 6: // double + return new DataDouble(((NBTBase.NBTPrimitive) nbt).func_150286_g()); + case 7: // byte[] + return new DataByteArray(((NBTTagByteArray) nbt).func_150292_c(), immutable); + case 8: // string + return new DataString(((NBTTagString) nbt).func_150285_a_()); + case 9: { // list + List values = new ArrayList(); + List original = MineTweakerHacks.getTagList((NBTTagList) nbt); + for (NBTBase value : original) { + values.add(from(value, immutable)); + } + return new DataList(values, immutable); + } + case 10: { // compound + Map values = new HashMap(); + NBTTagCompound original = (NBTTagCompound) nbt; + for (String key : (Set)original.func_150296_c()) { + values.put(key, from(original.getTag(key), immutable)); + } + return new DataMap(values, immutable); + } + case 11: // int[] + return new DataIntArray(((NBTTagIntArray) nbt).func_150302_c(), immutable); + default: + throw new RuntimeException("Unknown tag type: " + nbt.getId()); + } + } + + private NBTConverter() {} + + @Override + public NBTBase fromBool(boolean value) { + return new NBTTagInt(value ? 1 : 0); + } + + @Override + public NBTBase fromByte(byte value) { + return new NBTTagByte(value); + } + + @Override + public NBTBase fromShort(short value) { + return new NBTTagShort(value); + } + + @Override + public NBTBase fromInt(int value) { + return new NBTTagInt(value); + } + + @Override + public NBTBase fromLong(long value) { + return new NBTTagLong(value); + } + + @Override + public NBTBase fromFloat(float value) { + return new NBTTagFloat(value); + } + + @Override + public NBTBase fromDouble(double value) { + return new NBTTagDouble(value); + } + + @Override + public NBTBase fromString(String value) { + return new NBTTagString(value); + } + + @Override + public NBTBase fromList(List values) { + NBTTagList result = new NBTTagList(); + for (IData value : values) { + result.appendTag(from(value)); + } + return result; + } + + @Override + public NBTBase fromMap(Map values) { + NBTTagCompound result = new NBTTagCompound(); + for (Map.Entry entry : values.entrySet()) { + result.setTag(entry.getKey(), from(entry.getValue())); + } + return result; + } + + @Override + public NBTBase fromByteArray(byte[] value) { + return new NBTTagByteArray(value); + } + + @Override + public NBTBase fromIntArray(int[] value) { + return new NBTTagIntArray(value); + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/item/TweakerItemDefinition.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/item/TweakerItemDefinition.java new file mode 100644 index 0000000..908ca45 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/item/TweakerItemDefinition.java @@ -0,0 +1,22 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.item; + +import minetweaker.minecraft.item.IItemDefinition; +import net.minecraft.item.Item; + +/** + * + * @author Stan + */ +public class TweakerItemDefinition implements IItemDefinition { + private final Item item; + + public TweakerItemDefinition(Item item) { + this.item = item; + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/item/TweakerItemStack.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/item/TweakerItemStack.java new file mode 100644 index 0000000..6fd821b --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/item/TweakerItemStack.java @@ -0,0 +1,203 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.item; + +import java.util.Collections; +import java.util.List; +import minetweaker.MineTweakerAPI; +import minetweaker.mc172.data.NBTConverter; +import minetweaker.mc172.liquid.TweakerLiquidStack; +import minetweaker.minecraft.data.IData; +import minetweaker.minecraft.item.IIngredient; +import minetweaker.minecraft.item.IItemCondition; +import minetweaker.minecraft.item.IItemDefinition; +import minetweaker.minecraft.item.IItemStack; +import minetweaker.minecraft.item.IItemTransformer; +import minetweaker.minecraft.item.IngredientItem; +import minetweaker.minecraft.liquid.ILiquidStack; +import minetweaker.minecraft.util.ArrayUtil; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.FluidContainerRegistry; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.oredict.OreDictionary; + +/** + * + * @author Stan + */ +public class TweakerItemStack implements IItemStack { + private final ItemStack stack; + private final List items; + private IData tag = null; + + public TweakerItemStack(ItemStack itemStack) { + stack = itemStack; + items = Collections.singletonList(this); + } + + private TweakerItemStack(ItemStack itemStack, IData tag) { + stack = itemStack; + items = Collections.singletonList(this); + this.tag = tag; + } + + @Override + public IItemDefinition getDefinition() { + return new TweakerItemDefinition(stack.getItem()); + } + + @Override + public String getName() { + return stack.getUnlocalizedName(); + } + + @Override + public String getDisplayName() { + return stack.getDisplayName(); + } + + @Override + public int getDamage() { + return stack.getItemDamage(); + } + + @Override + public IData getTag() { + if (tag == null) { + if (stack.stackTagCompound == null) { + return null; + } + + tag = NBTConverter.from(stack.stackTagCompound, true); + } + return tag; + } + + @Override + public int getMaxDamage() { + return stack.getMaxDamage(); + } + + @Override + public ILiquidStack getLiquid() { + FluidStack liquid = FluidContainerRegistry.getFluidForFilledItem(stack); + return liquid == null ? null : new TweakerLiquidStack(liquid); + } + + @Override + public IIngredient anyDamage() { + if (stack.getItem().getHasSubtypes()) { + MineTweakerAPI.logger.logWarning("subitems don't have damaged states"); + return this; + } else { + ItemStack result = new ItemStack(stack.getItem(), stack.stackSize, OreDictionary.WILDCARD_VALUE); + result.stackTagCompound = stack.stackTagCompound; + return new TweakerItemStack(result, tag); + } + } + + @Override + public IItemStack withDamage(int damage) { + if (stack.getItem().getHasSubtypes()) { + MineTweakerAPI.logger.logWarning("subitems don't have damaged states"); + return this; + } else { + ItemStack result = new ItemStack(stack.getItem(), stack.stackSize, damage); + result.stackTagCompound = stack.stackTagCompound; + return new TweakerItemStack(result, tag); + } + } + + @Override + public IItemStack withAmount(int amount) { + ItemStack result = new ItemStack(stack.getItem(), amount, stack.getItemDamage()); + result.stackTagCompound = stack.stackTagCompound; + return new TweakerItemStack(result, tag); + } + + @Override + public IItemStack withTag(IData tag) { + ItemStack result = new ItemStack(stack.getItem(), stack.stackSize, stack.getItemDamage()); + result.stackTagCompound = (NBTTagCompound) NBTConverter.from(tag); + return new TweakerItemStack(result, tag); + } + + @Override + public IItemStack updateTag(IData tagUpdate) { + if (tag == null) { + if (stack.stackTagCompound == null) { + return withTag(tagUpdate); + } + + tag = NBTConverter.from(stack.stackTagCompound, true); + } + + IData updated = tag.update(tagUpdate); + return withTag(updated); + } + + @Override + public String getMark() { + return null; + } + + @Override + public int getAmount() { + return stack.stackSize; + } + + @Override + public List getItems() { + return items; + } + + @Override + public IIngredient amount(int amount) { + return withAmount(amount); + } + + @Override + public IIngredient transform(IItemTransformer transformer) { + return new IngredientItem(this, null, ArrayUtil.EMPTY_CONDITIONS, new IItemTransformer[] { transformer }); + } + + @Override + public IIngredient only(IItemCondition condition) { + return new IngredientItem(this, null, new IItemCondition[] { condition }, ArrayUtil.EMPTY_TRANSFORMERS); + } + + @Override + public IIngredient marked(String mark) { + return new IngredientItem(this, mark, ArrayUtil.EMPTY_CONDITIONS, ArrayUtil.EMPTY_TRANSFORMERS); + } + + @Override + public boolean matches(IItemStack item) { + ItemStack internal = (ItemStack) item.getInternal(); + return internal.getItem() == stack.getItem() + && internal.stackSize == stack.stackSize + && (stack.getItemDamage() == OreDictionary.WILDCARD_VALUE || stack.getItemDamage() == internal.getItemDamage()); + } + + @Override + public boolean contains(IIngredient ingredient) { + List iitems = ingredient.getItems(); + if (iitems == null || iitems.size() != 1) return false; + return matches(iitems.get(0)); + } + + @Override + public IItemStack applyTransform(IItemStack item) { + return item; + } + + @Override + public Object getInternal() { + return stack; + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/liquid/TweakerLiquidDefinition.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/liquid/TweakerLiquidDefinition.java new file mode 100644 index 0000000..b52af13 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/liquid/TweakerLiquidDefinition.java @@ -0,0 +1,260 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.liquid; + +import minetweaker.IUndoableAction; +import minetweaker.MineTweakerAPI; +import minetweaker.minecraft.liquid.ILiquidDefinition; +import minetweaker.minecraft.liquid.ILiquidStack; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +/** + * + * @author Stan + */ +public class TweakerLiquidDefinition implements ILiquidDefinition { + private final Fluid fluid; + + public TweakerLiquidDefinition(Fluid fluid) { + this.fluid = fluid; + } + + @Override + public ILiquidStack asStack(int millibuckets) { + return new TweakerLiquidStack(new FluidStack(fluid, millibuckets)); + } + + @Override + public int getLuminosity() { + return fluid.getLuminosity(); + } + + @Override + public void setLuminosity(int value) { + MineTweakerAPI.tweaker.apply(new ActionSetLuminosity(value)); + } + + @Override + public int getDensity() { + return fluid.getDensity(); + } + + @Override + public void setDensity(int density) { + MineTweakerAPI.tweaker.apply(new ActionSetDensity(density)); + } + + @Override + public int getTemperature() { + return fluid.getTemperature(); + } + + @Override + public void setTemperature(int temperature) { + MineTweakerAPI.tweaker.apply(new ActionSetTemperature(temperature)); + } + + @Override + public int getViscosity() { + return fluid.getViscosity(); + } + + @Override + public void setViscosity(int viscosity) { + MineTweakerAPI.tweaker.apply(new ActionSetViscosity(viscosity)); + } + + @Override + public boolean isGaseous() { + return fluid.isGaseous(); + } + + @Override + public void setGaseous(boolean gaseous) { + MineTweakerAPI.tweaker.apply(new ActionSetGaseous(gaseous)); + } + + // ###################### + // ### Action classes ### + // ###################### + + private class ActionSetLuminosity implements IUndoableAction { + private final int oldValue; + private final int newValue; + + public ActionSetLuminosity(int newValue) { + oldValue = getLuminosity(); + this.newValue = newValue; + } + + @Override + public void apply() { + fluid.setLuminosity(newValue); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + fluid.setLuminosity(oldValue); + } + + @Override + public String describe() { + return "Setting " + fluid.getName() + " luminosity to " + newValue; + } + + @Override + public String describeUndo() { + return "Restoring " + fluid.getName() + " luminosity to " + oldValue; + } + } + + private class ActionSetDensity implements IUndoableAction { + private final int oldValue; + private final int newValue; + + public ActionSetDensity(int newValue) { + oldValue = getDensity(); + this.newValue = newValue; + } + + @Override + public void apply() { + fluid.setDensity(newValue); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + fluid.setDensity(oldValue); + } + + @Override + public String describe() { + return "Setting " + fluid.getName() + " density to " + newValue; + } + + @Override + public String describeUndo() { + return "Restoring " + fluid.getName() + " density to " + oldValue; + } + } + + private class ActionSetTemperature implements IUndoableAction { + private final int oldValue; + private final int newValue; + + public ActionSetTemperature(int newValue) { + oldValue = getTemperature(); + this.newValue = newValue; + } + + @Override + public void apply() { + fluid.setTemperature(newValue); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + fluid.setTemperature(oldValue); + } + + @Override + public String describe() { + return "Setting " + fluid.getName() + " temperature to " + newValue; + } + + @Override + public String describeUndo() { + return "Restoring " + fluid.getName() + " temperature to " + oldValue; + } + } + + private class ActionSetViscosity implements IUndoableAction { + private final int oldValue; + private final int newValue; + + public ActionSetViscosity(int newValue) { + oldValue = getViscosity(); + this.newValue = newValue; + } + + @Override + public void apply() { + fluid.setViscosity(newValue); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + fluid.setViscosity(oldValue); + } + + @Override + public String describe() { + return "Setting " + fluid.getName() + " viscosity to " + newValue; + } + + @Override + public String describeUndo() { + return "Restoring " + fluid.getName() + " viscosity to " + oldValue; + } + } + + private class ActionSetGaseous implements IUndoableAction { + private final boolean oldValue; + private final boolean newValue; + + public ActionSetGaseous(boolean newValue) { + oldValue = isGaseous(); + this.newValue = newValue; + } + + @Override + public void apply() { + fluid.setGaseous(newValue); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + fluid.setGaseous(oldValue); + } + + @Override + public String describe() { + return "Setting " + fluid.getName() + " gaseous to " + newValue; + } + + @Override + public String describeUndo() { + return "Restoring " + fluid.getName() + " gaseous to " + oldValue; + } + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/liquid/TweakerLiquidStack.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/liquid/TweakerLiquidStack.java new file mode 100644 index 0000000..0006e9f --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/liquid/TweakerLiquidStack.java @@ -0,0 +1,99 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.liquid; + +import minetweaker.mc172.data.NBTConverter; +import minetweaker.minecraft.data.IData; +import minetweaker.minecraft.liquid.ILiquidDefinition; +import minetweaker.minecraft.liquid.ILiquidStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.FluidStack; + +/** + * + * @author Stan + */ +public class TweakerLiquidStack implements ILiquidStack { + private final FluidStack stack; + private IData tag = null; + + public TweakerLiquidStack(FluidStack stack) { + this.stack = stack; + } + + private TweakerLiquidStack(FluidStack stack, IData tag) { + this.stack = stack; + this.tag = tag; + } + + @Override + public ILiquidDefinition getDefinition() { + return new TweakerLiquidDefinition(stack.getFluid()); + } + + @Override + public int getAmount() { + return stack.amount; + } + + @Override + public IData getTag() { + if (tag == null) { + if (stack.tag == null) { + return null; + } + + tag = NBTConverter.from(stack.tag, true); + } + + return tag; + } + + @Override + public ILiquidStack withTag(IData data) { + FluidStack result = new FluidStack(stack.fluidID, stack.amount); + result.tag = (NBTTagCompound) NBTConverter.from(data); + return new TweakerLiquidStack(result, data.immutable()); + } + + @Override + public ILiquidStack withAmount(int amount) { + FluidStack result = new FluidStack(stack.fluidID, amount); + result.tag = stack.tag; + return new TweakerLiquidStack(result, tag); + } + + @Override + public int getLuminosity() { + return stack.getFluid().getLuminosity(stack); + } + + @Override + public int getDensity() { + return stack.getFluid().getDensity(stack); + } + + @Override + public int getTemperature() { + return stack.getFluid().getTemperature(stack); + } + + @Override + public int getViscosity() { + return stack.getFluid().getViscosity(stack); + } + + @Override + public boolean isGaseous() { + return stack.getFluid().isGaseous(stack); + } + + @Override + public Object getInternal() { + return stack; + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/oredict/OreDict.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/oredict/OreDict.java new file mode 100644 index 0000000..4787d3b --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/oredict/OreDict.java @@ -0,0 +1,22 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.oredict; + +import minetweaker.minecraft.oredict.IOreDict; +import minetweaker.minecraft.oredict.IOreDictEntry; +import net.minecraftforge.oredict.OreDictionary; + +/** + * + * @author Stan + */ +public class OreDict implements IOreDict { + @Override + public IOreDictEntry get(String name) { + return new OreDictEntry(OreDictionary.getOreID(name)); + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/oredict/OreDictEntry.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/oredict/OreDictEntry.java new file mode 100644 index 0000000..b62c60a --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/oredict/OreDictEntry.java @@ -0,0 +1,312 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.oredict; + +import java.util.ArrayList; +import java.util.List; +import minetweaker.IUndoableAction; +import minetweaker.MineTweakerAPI; +import minetweaker.mc172.item.TweakerItemStack; +import minetweaker.mc172.util.MineTweakerHacks; +import minetweaker.minecraft.item.IIngredient; +import minetweaker.minecraft.item.IItemCondition; +import minetweaker.minecraft.item.IItemStack; +import minetweaker.minecraft.item.IItemTransformer; +import minetweaker.minecraft.item.IngredientStack; +import minetweaker.minecraft.oredict.IOreDictEntry; +import minetweaker.minecraft.oredict.IngredientOreDict; +import minetweaker.minecraft.util.ArrayUtil; +import net.minecraft.item.ItemStack; +import net.minecraftforge.oredict.OreDictionary; + +/** + * + * @author Stan + */ +public class OreDictEntry implements IOreDictEntry { + private final Integer id; + + public OreDictEntry(Integer id) { + this.id = id; + } + + // #################################### + // ### IOreDictEntry implementation ### + // #################################### + + @Override + public void add(IItemStack item) { + ItemStack stack = (ItemStack) item.getInternal(); + if (stack == null) { + MineTweakerAPI.logger.logError("not a valid item"); + } else { + MineTweakerAPI.tweaker.apply(new ActionAddItem(id, stack)); + } + } + + @Override + public void addAll(IOreDictEntry entry) { + if (entry instanceof OreDictEntry) { + MineTweakerAPI.tweaker.apply(new ActionAddAll(id, ((OreDictEntry) entry).id)); + } else { + MineTweakerAPI.logger.logError("not a valid entry"); + } + } + + @Override + public void remove(IItemStack item) { + ItemStack result = null; + for (ItemStack itemStack : OreDictionary.getOres(id)) { + if (item.matches(new TweakerItemStack(itemStack))) { + result = itemStack; + break; + } + } + + if (result != null) { + MineTweakerAPI.tweaker.apply(new ActionRemoveItem(id, result)); + } + } + + @Override + public boolean contains(IItemStack item) { + for (ItemStack itemStack : OreDictionary.getOres(id)) { + if (item.matches(new TweakerItemStack(itemStack))) { + return true; + } + } + + return false; + } + + @Override + public void mirror(IOreDictEntry other) { + if (other instanceof OreDictEntry) { + MineTweakerAPI.tweaker.apply(new ActionMirror(id, ((OreDictEntry) other).id)); + } else { + MineTweakerAPI.logger.logError("not a valid oredict entry"); + } + } + + @Override + public String getMark() { + return null; + } + + @Override + public int getAmount() { + return 1; + } + + @Override + public List getItems() { + List result = new ArrayList(); + for (ItemStack item : OreDictionary.getOres(id)) { + result.add(new TweakerItemStack(item)); + } + return result; + } + + @Override + public IIngredient amount(int amount) { + return new IngredientStack(this, amount); + } + + @Override + public IIngredient transform(IItemTransformer transformer) { + return new IngredientOreDict(this, null, ArrayUtil.EMPTY_CONDITIONS, new IItemTransformer[] { transformer }); + } + + @Override + public IIngredient only(IItemCondition condition) { + return new IngredientOreDict(this, null, new IItemCondition[] { condition }, ArrayUtil.EMPTY_TRANSFORMERS); + } + + @Override + public IIngredient marked(String mark) { + return new IngredientOreDict(this, mark, ArrayUtil.EMPTY_CONDITIONS, ArrayUtil.EMPTY_TRANSFORMERS); + } + + @Override + public boolean matches(IItemStack item) { + return contains(item); + } + + @Override + public boolean contains(IIngredient ingredient) { + List items = ingredient.getItems(); + for (IItemStack item : items) { + if (!matches(item)) return false; + } + + return true; + } + + @Override + public IItemStack applyTransform(IItemStack item) { + return item; + } + + @Override + public Object getInternal() { + return OreDictionary.getOreName(id); + } + + // ###################### + // ### Action classes ### + // ###################### + + private static class ActionAddItem implements IUndoableAction { + private final Integer id; + private final ItemStack item; + + public ActionAddItem(Integer id, ItemStack item) { + this.id = id; + this.item = item; + } + + @Override + public void apply() { + OreDictionary.registerOre(id, item); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + OreDictionary.getOres(id).remove(item); + } + + @Override + public String describe() { + return "Adding " + item.getDisplayName() + " to ore dictionary entry " + OreDictionary.getOreName(id); + } + + @Override + public String describeUndo() { + return "Removing " + item.getDisplayName() + " from ore dictionary entry " + OreDictionary.getOreName(id); + } + } + + private static class ActionMirror implements IUndoableAction { + private final Integer idTarget; + private final Integer idSource; + + private final ArrayList targetCopy; + + public ActionMirror(Integer idTarget, Integer idSource) { + this.idTarget = idTarget; + this.idSource = idSource; + + targetCopy = OreDictionary.getOres(idTarget); + } + + @Override + public void apply() { + MineTweakerHacks.getOreStacks().put(idTarget, OreDictionary.getOres(idSource)); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + MineTweakerHacks.getOreStacks().put(idTarget, targetCopy); + } + + @Override + public String describe() { + return "Mirroring " + OreDictionary.getOreName(idSource) + " to " + OreDictionary.getOreName(idTarget); + } + + @Override + public String describeUndo() { + return "Undoing mirror of " + OreDictionary.getOreName(idSource) + " to " + OreDictionary.getOreName(idTarget); + } + } + + private static class ActionRemoveItem implements IUndoableAction { + private final Integer id; + private final ItemStack item; + + public ActionRemoveItem(Integer id, ItemStack item) { + this.id = id; + this.item = item; + } + + @Override + public void apply() { + OreDictionary.getOres(id).add(item); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + OreDictionary.getOres(id).remove(item); + } + + @Override + public String describe() { + return "Adding " + item.getDisplayName() + " to ore dictionary entry " + OreDictionary.getOreName(id); + } + + @Override + public String describeUndo() { + return "Removing " + item.getDisplayName() + " from ore dictionary entry " + OreDictionary.getOreName(id); + } + } + + private static class ActionAddAll implements IUndoableAction { + private final Integer idTarget; + private final Integer idSource; + + public ActionAddAll(Integer idTarget, Integer idSource) { + this.idTarget = idTarget; + this.idSource = idSource; + } + + @Override + public void apply() { + List ores = OreDictionary.getOres(idTarget); + for (ItemStack stack : OreDictionary.getOres(idSource)) { + ores.add(stack); + } + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + List ores = OreDictionary.getOres(idTarget); + for (ItemStack stack : OreDictionary.getOres(idSource)) { + ores.remove(stack); + } + } + + @Override + public String describe() { + return "Copying contents of ore dictionary entry " + OreDictionary.getOreName(idSource) + " to " + OreDictionary.getOreName(idTarget); + } + + @Override + public String describeUndo() { + return "Removing contents of ore dictionary entry " + OreDictionary.getOreName(idSource) + " from " + OreDictionary.getOreName(idTarget); + } + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/MTRecipeManager.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/MTRecipeManager.java new file mode 100644 index 0000000..51e6bb5 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/MTRecipeManager.java @@ -0,0 +1,221 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.recipes; + +import minetweaker.minecraft.recipes.ShapedRecipe; +import minetweaker.minecraft.recipes.ShapelessRecipe; +import java.util.ArrayList; +import java.util.List; +import minetweaker.IUndoableAction; +import minetweaker.MineTweakerAPI; +import minetweaker.mc172.item.TweakerItemStack; +import minetweaker.mc172.util.MineTweakerHacks;import minetweaker.minecraft.item.IIngredient; +import minetweaker.minecraft.item.IItemStack; +; +import minetweaker.minecraft.recipes.IRecipeFunction; +import minetweaker.minecraft.recipes.IRecipeManager; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.item.crafting.ShapedRecipes; +import net.minecraft.item.crafting.ShapelessRecipes; +import net.minecraftforge.oredict.ShapedOreRecipe; +import net.minecraftforge.oredict.ShapelessOreRecipe; + +/** + * + * @author Stan + */ +public class MTRecipeManager implements IRecipeManager { + private final List recipes; + + public MTRecipeManager() { + recipes = MineTweakerHacks.getRecipes(); + } + + @Override + public int remove(IIngredient output) { + List toRemove = new ArrayList(); + List removeIndex = new ArrayList(); + for (int i = 0; i < recipes.size(); i++) { + IRecipe recipe = recipes.get(i); + + if (output.matches(new TweakerItemStack(recipe.getRecipeOutput()))) { + toRemove.add(recipe); + removeIndex.add(i); + } + } + + MineTweakerAPI.tweaker.apply(new ActionRemoveRecipes(toRemove, removeIndex)); + return toRemove.size(); + } + + @Override + public void addShaped(IItemStack output, IIngredient[][] ingredients, IRecipeFunction function, boolean mirrored) { + if (output.getInternal() == null) { + MineTweakerAPI.logger.logError("invalid output item"); + return; + } + + ShapedRecipe recipe = new ShapedRecipe(output, ingredients, function, mirrored); + IRecipe irecipe = RecipeConverter.convert(recipe); + MineTweakerAPI.tweaker.apply(new ActionAddRecipe(irecipe)); + } + + @Override + public void addShapeless(IItemStack output, IIngredient[] ingredients, IRecipeFunction function) { + if (output.getInternal() == null) { + MineTweakerAPI.logger.logError("invalid output item"); + return; + } + + ShapelessRecipe recipe = new ShapelessRecipe(output, ingredients, function); + IRecipe irecipe = RecipeConverter.convert(recipe); + MineTweakerAPI.tweaker.apply(new ActionAddRecipe(irecipe)); + } + + @Override + public int removeShaped(IIngredient output, IIngredient[][] ingredients) { + List toRemove = new ArrayList(); + List removeIndex = new ArrayList(); + for (int i = 0; i < recipes.size(); i++) { + IRecipe recipe = recipes.get(i); + + if (!output.matches(new TweakerItemStack(recipe.getRecipeOutput()))) { + continue; + } + + if (recipe instanceof ShapedRecipes) { + ShapedRecipes srecipe = (ShapedRecipes) recipe; + // TODO: check contents + } else if (recipe instanceof ShapedOreRecipe) { + ShapedOreRecipe srecipe = (ShapedOreRecipe) recipe; + // TODO: check contents + } + + toRemove.add(recipe); + removeIndex.add(i); + } + + MineTweakerAPI.tweaker.apply(new ActionRemoveRecipes(toRemove, removeIndex)); + return toRemove.size(); + } + + @Override + public int removeShapeless(IIngredient output, IIngredient[] ingredients, boolean wildcard) { + List toRemove = new ArrayList(); + List removeIndex = new ArrayList(); + for (int i = 0; i < recipes.size(); i++) { + IRecipe recipe = recipes.get(i); + + if (!output.matches(new TweakerItemStack(recipe.getRecipeOutput()))) { + continue; + } + + if (recipe instanceof ShapelessRecipes) { + ShapelessRecipes srecipe = (ShapelessRecipes) recipe; + // TODO: check contents + } else if (recipe instanceof ShapelessOreRecipe) { + ShapelessOreRecipe srecipe = (ShapelessOreRecipe) recipe; + ArrayList inputs = srecipe.getInput(); + if (inputs.size() < ingredients.length) { + continue; + } if (!wildcard && inputs.size() > ingredients.length) { + continue; + } + + boolean[] tag = new boolean[ingredients.length]; + for (Object object : inputs) { + + } + + // TODO: check contents + } + + toRemove.add(recipe); + removeIndex.add(i); + } + + MineTweakerAPI.tweaker.apply(new ActionRemoveRecipes(toRemove, removeIndex)); + return toRemove.size(); + } + + @Override + public IItemStack craft(IItemStack[][] contents) { + return null; // TODO: implement + } + + private class ActionRemoveRecipes implements IUndoableAction { + private final List removingIndices; + private final List removingRecipes; + + public ActionRemoveRecipes(List recipes, List indices) { + this.removingIndices = indices; + this.removingRecipes = recipes; + } + + @Override + public void apply() { + for (int i = removingIndices.size() - 1; i >= 0; i--) { + recipes.remove((int) removingIndices.get(i)); + } + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + for (int i = 0; i < removingIndices.size(); i++) { + recipes.add(removingIndices.get(i), removingRecipes.get(i)); + } + } + + @Override + public String describe() { + return "Removing " + removingIndices.size() + " recipes"; + } + + @Override + public String describeUndo() { + return "Restoring " + removingIndices.size() + " recipes"; + } + } + + private class ActionAddRecipe implements IUndoableAction { + private final IRecipe recipe; + + public ActionAddRecipe(IRecipe recipe) { + this.recipe = recipe; + } + + @Override + public void apply() { + recipes.add(recipe); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + recipes.remove(recipe); + } + + @Override + public String describe() { + return "Adding recipe for " + recipe.getRecipeOutput().getDisplayName(); + } + + @Override + public String describeUndo() { + return "Removing recipe for " + recipe.getRecipeOutput().getDisplayName(); + } + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/RecipeConverter.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/RecipeConverter.java new file mode 100644 index 0000000..d3a5c92 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/RecipeConverter.java @@ -0,0 +1,119 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.recipes; + +import minetweaker.minecraft.recipes.ShapedRecipe; +import minetweaker.minecraft.recipes.ShapelessRecipe; +import java.util.ArrayList; +import java.util.Arrays; +import minetweaker.minecraft.item.IIngredient; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipe; + +/** + * + * @author Stan + */ +public class RecipeConverter { + private RecipeConverter() {} + + public static final int TYPE_ADVANCED = 0; + public static final int TYPE_ORE = 1; + public static final int TYPE_BASIC = 2; + + private static int getIngredientType(IIngredient pattern) { + Object internal = pattern.getInternal(); + if (internal == null) { + return TYPE_ADVANCED; + } else if (internal instanceof ItemStack) { + return TYPE_BASIC; + } else { + return TYPE_ORE; + } + } + + private static int getRecipeType(IIngredient[] ingredients) { + int type = TYPE_BASIC; + for (IIngredient ingredient : ingredients) { + type = Math.min(type, getIngredientType(ingredient)); + } + return type; + } + + public static IRecipe convert(ShapelessRecipe recipe) { + IIngredient[] ingredients = recipe.getIngredients(); + int type = getRecipeType(ingredients); + + if (type == TYPE_BASIC) { + ItemStack[] items = new ItemStack[ingredients.length]; + for (int i = 0; i < ingredients.length; i++) { + items[i] = (ItemStack) ingredients[i].getInternal(); + } + return new ShapelessRecipeBasic(items, recipe); + } else if (type == TYPE_ORE) { + Object[] items = new Object[ingredients.length]; + for (int i = 0; i < ingredients.length; i++) { + items[i] = ingredients[i].getInternal(); + } + return new ShapelessRecipeOre(items, recipe); + } else { + return new ShapelessRecipeAdvanced(recipe); + } + } + + public static IRecipe convert(ShapedRecipe recipe) { + IIngredient[] ingredients = recipe.getIngredients(); + byte[] posx = recipe.getIngredientsX(); + byte[] posy = recipe.getIngredientsY(); + + // determine recipe type + int type = getRecipeType(ingredients); + + // construct recipe + if (type == TYPE_BASIC) { + ItemStack[] basicIngredients = new ItemStack[recipe.getHeight() * recipe.getWidth()]; + for (int i = 0; i < ingredients.length; i++) { + basicIngredients[posx[i] + posy[i] * recipe.getWidth()] = (ItemStack) ingredients[i].getInternal(); + } + + return new ShapedRecipeBasic(basicIngredients, recipe); + } else if (type == TYPE_ORE) { + Object[] converted = new Object[recipe.getHeight() * recipe.getWidth()]; + for (int i = 0; i < ingredients.length; i++) { + converted[posx[i] + posy[i] * recipe.getWidth()] = ingredients[i].getInternal(); + } + + // arguments contents: + // 1) recipe patterns + // 2) characters + ingredients + + int counter = 0; + String[] parts = new String[recipe.getHeight()]; + ArrayList rarguments = new ArrayList(); + for (int i = 0; i < recipe.getHeight(); i++) { + char[] pattern = new char[recipe.getWidth()]; + for (int j = 0; j < recipe.getWidth(); j++) { + int off = i * recipe.getWidth() + j; + if (converted[off] == null) { + pattern[j] = ' '; + } else { + pattern[j] = (char) ('A' + counter); + rarguments.add(pattern[j]); + rarguments.add(converted[off]); + } + } + parts[i] = new String(pattern); + } + + rarguments.addAll(0, Arrays.asList(parts)); + + return new ShapedRecipeOre(rarguments.toArray(), recipe); + } else { + return new ShapedRecipeAdvanced(recipe); + } + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeAdvanced.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeAdvanced.java new file mode 100644 index 0000000..f0042ad --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeAdvanced.java @@ -0,0 +1,46 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.recipes; + +import minetweaker.minecraft.recipes.ShapedRecipe; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.world.World; + +/** + * TODO: NEI integration for this kind of recipes. + * + * @author Stan + */ +public class ShapedRecipeAdvanced implements IRecipe { + private final ShapedRecipe recipe; + + public ShapedRecipeAdvanced(ShapedRecipe recipe) { + this.recipe = recipe; + } + + @Override + public boolean matches(InventoryCrafting inventory, World world) { + return recipe.matches(TweakerCraftingInventory.get(inventory)); + } + + @Override + public ItemStack getCraftingResult(InventoryCrafting inventory) { + return (ItemStack) recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)).getInternal(); + } + + @Override + public int getRecipeSize() { + return recipe.getWidth() * recipe.getHeight(); + } + + @Override + public ItemStack getRecipeOutput() { + return (ItemStack) recipe.getOutput().getInternal(); + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeBasic.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeBasic.java new file mode 100644 index 0000000..6e0aa8e --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeBasic.java @@ -0,0 +1,37 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.recipes; + +import minetweaker.minecraft.recipes.ShapedRecipe; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.ShapedRecipes; +import net.minecraft.world.World; + +/** + * + * @author Stan + */ +public class ShapedRecipeBasic extends ShapedRecipes { + private final ShapedRecipe recipe; + + public ShapedRecipeBasic(ItemStack[] basicInputs, ShapedRecipe recipe) { + super(recipe.getWidth(), recipe.getHeight(), basicInputs, (ItemStack) recipe.getOutput().getInternal()); + + this.recipe = recipe; + } + + @Override + public boolean matches(InventoryCrafting inventory, World world) { + return recipe.matches(TweakerCraftingInventory.get(inventory)); + } + + @Override + public ItemStack getCraftingResult(InventoryCrafting inventory) { + return (ItemStack) recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)).getInternal(); + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeOre.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeOre.java new file mode 100644 index 0000000..8f25bd0 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapedRecipeOre.java @@ -0,0 +1,37 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.recipes; + +import minetweaker.minecraft.recipes.ShapedRecipe; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.oredict.ShapedOreRecipe; + +/** + * + * @author Stan + */ +public class ShapedRecipeOre extends ShapedOreRecipe { + private final ShapedRecipe recipe; + + public ShapedRecipeOre(Object[] contents, ShapedRecipe recipe) { + super((ItemStack) recipe.getOutput().getInternal(), contents); + + this.recipe = recipe; + } + + @Override + public boolean matches(InventoryCrafting inventory, World world) { + return recipe.matches(TweakerCraftingInventory.get(inventory)); + } + + @Override + public ItemStack getCraftingResult(InventoryCrafting inventory) { + return (ItemStack) recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)).getInternal(); + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeAdvanced.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeAdvanced.java new file mode 100644 index 0000000..593516d --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeAdvanced.java @@ -0,0 +1,45 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.recipes; + +import minetweaker.minecraft.recipes.ShapelessRecipe; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.world.World; + +/** + * + * @author Stan + */ +public class ShapelessRecipeAdvanced implements IRecipe { + private final ShapelessRecipe recipe; + + public ShapelessRecipeAdvanced(ShapelessRecipe recipe) { + this.recipe = recipe; + } + + @Override + public boolean matches(InventoryCrafting inventory, World world) { + return recipe.matches(TweakerCraftingInventory.get(inventory)); + } + + @Override + public ItemStack getCraftingResult(InventoryCrafting inventory) { + return (ItemStack) recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)).getInternal(); + } + + @Override + public int getRecipeSize() { + return recipe.getSize(); + } + + @Override + public ItemStack getRecipeOutput() { + return (ItemStack) recipe.getOutput().getInternal(); + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeBasic.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeBasic.java new file mode 100644 index 0000000..d543640 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeBasic.java @@ -0,0 +1,38 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.recipes; + +import minetweaker.minecraft.recipes.ShapelessRecipe; +import java.util.Arrays; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.ShapelessRecipes; +import net.minecraft.world.World; + +/** + * + * @author Stan + */ +public class ShapelessRecipeBasic extends ShapelessRecipes { + private final ShapelessRecipe recipe; + + public ShapelessRecipeBasic(ItemStack[] ingredients, ShapelessRecipe recipe) { + super((ItemStack) recipe.getOutput().getInternal(), Arrays.asList(ingredients)); + + this.recipe = recipe; + } + + @Override + public boolean matches(InventoryCrafting inventory, World world) { + return recipe.matches(TweakerCraftingInventory.get(inventory)); + } + + @Override + public ItemStack getCraftingResult(InventoryCrafting inventory) { + return (ItemStack) recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)).getInternal(); + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeOre.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeOre.java new file mode 100644 index 0000000..0aea1cb --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/ShapelessRecipeOre.java @@ -0,0 +1,37 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.recipes; + +import minetweaker.minecraft.recipes.ShapelessRecipe; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.oredict.ShapelessOreRecipe; + +/** + * + * @author Stan + */ +public class ShapelessRecipeOre extends ShapelessOreRecipe { + private final ShapelessRecipe recipe; + + public ShapelessRecipeOre(Object[] ingredients, ShapelessRecipe recipe) { + super((ItemStack) recipe.getOutput().getInternal(), ingredients); + + this.recipe = recipe; + } + + @Override + public boolean matches(InventoryCrafting inventory, World world) { + return recipe.matches(TweakerCraftingInventory.get(inventory)); + } + + @Override + public ItemStack getCraftingResult(InventoryCrafting inventory) { + return (ItemStack) recipe.getCraftingResult(TweakerCraftingInventory.get(inventory)).getInternal(); + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/TweakerCraftingInventory.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/TweakerCraftingInventory.java new file mode 100644 index 0000000..3d9e3df --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/recipes/TweakerCraftingInventory.java @@ -0,0 +1,84 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.recipes; + +import minetweaker.mc172.item.TweakerItemStack; +import minetweaker.minecraft.item.IItemStack; +import minetweaker.minecraft.recipes.ICraftingInventory; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; + +/** + * + * @author Stan + */ +public class TweakerCraftingInventory implements ICraftingInventory { + private static final ThreadLocal cache = new ThreadLocal(); + + public static TweakerCraftingInventory get(InventoryCrafting inventory) { + if (cache.get() == null || cache.get().inventory != inventory) { + TweakerCraftingInventory result = new TweakerCraftingInventory(inventory); + cache.set(result); + return result; + } else { + return cache.get(); + } + } + + private final int width; + private final int height; + private final InventoryCrafting inventory; + private final IItemStack[] stacks; + + private TweakerCraftingInventory(InventoryCrafting inventory) { + this.inventory = inventory; + width = height = (int) Math.sqrt(inventory.getSizeInventory()); + stacks = new IItemStack[width * height]; + for (int i = 0; i < inventory.getSizeInventory(); i++) { + if (inventory.getStackInSlot(i) != null) { + stacks[i] = new TweakerItemStack(inventory.getStackInSlot(i)); + } + } + } + + @Override + public int getSize() { + return width * height; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public IItemStack getStack(int i) { + return stacks[i]; + } + + @Override + public IItemStack getStack(int x, int y) { + return stacks[y * width + x]; + } + + @Override + public void setStack(int x, int y, IItemStack stack) { + stacks[y * width + x] = stack; + inventory.setInventorySlotContents(y * width + x, (ItemStack) stack.getInternal()); + } + + @Override + public void setStack(int i, IItemStack stack) { + stacks[i] = stack; + inventory.setInventorySlotContents(i, (ItemStack) stack.getInternal()); + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/transformer/MineTweakerASM.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/transformer/MineTweakerASM.java new file mode 100644 index 0000000..d18a710 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/transformer/MineTweakerASM.java @@ -0,0 +1,42 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.transformer; + +import java.util.List; +import net.minecraft.launchwrapper.IClassTransformer; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Opcodes; + +/** + * + * @author Stanneke + */ +public class MineTweakerASM implements IClassTransformer { + + @Override + public byte[] transform(String name, String newName, byte[] bytecode) { + ClassVisitor visitor = new AnnotationDetector(); + ClassReader reader = new ClassReader(bytecode); + reader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + return bytecode; + } + + private class AnnotationDetector extends ClassVisitor { + public AnnotationDetector() { + super(Opcodes.ASM4); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + System.out.println("Annotation: " + desc); + + return super.visitAnnotation(desc, visible); + } + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/transformer/MineTweakerTransformer.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/transformer/MineTweakerTransformer.java new file mode 100644 index 0000000..e1e62b8 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/transformer/MineTweakerTransformer.java @@ -0,0 +1,48 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.transformer; + +import cpw.mods.fml.relauncher.IFMLLoadingPlugin; +import cpw.mods.fml.relauncher.IFMLLoadingPlugin.MCVersion; +import java.util.Map; +import minetweaker.mc172.MineTweakerMod; + +/** + * + * @author Stanneke + */ +@MCVersion(MineTweakerMod.MCVERSION) +@IFMLLoadingPlugin.TransformerExclusions("minetweaker.mc172.transformer") +public class MineTweakerTransformer implements IFMLLoadingPlugin { + + @Override + public String[] getASMTransformerClass() { + System.out.println("MineTweakerASM transformer class loading"); + return new String[] { MineTweakerASM.class.getName() }; + } + + @Override + public String getModContainerClass() { + //return MineTweaker.class.getName(); + return null; + } + + @Override + public String getSetupClass() { + return null; + } + + @Override + public void injectData(Map data) { + } + + @Override + public String getAccessTransformerClass() { + return null; + } +} + diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/util/MineTweakerHacks.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/util/MineTweakerHacks.java new file mode 100644 index 0000000..7842c4e --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/util/MineTweakerHacks.java @@ -0,0 +1,84 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.util; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import minetweaker.MineTweakerAPI; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.CraftingManager; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.oredict.OreDictionary; + +/** + * Common class for all runtime hacks (stuff requiring reflection). It is not + * unexpected to have it break with new minecraft versions and may need regular + * adjustment - as such, those have been collected here. + * + * @author Stan Hebben + */ +public class MineTweakerHacks { + private static final Field NBTTAGLIST_TAGLIST; + private static final Field OREDICTIONARY_ORESTACKS; + + static { + NBTTAGLIST_TAGLIST = getField(NBTTagList.class, MineTweakerObfuscation.NBTTAGLIST_TAGLIST); + OREDICTIONARY_ORESTACKS = getField(OreDictionary.class, MineTweakerObfuscation.OREDICTIONARY_ORESTACKS); + } + + private MineTweakerHacks() {} + + public static List getTagList(NBTTagList list) { + if (NBTTAGLIST_TAGLIST == null) { + return null; + } + try { + return (List) NBTTAGLIST_TAGLIST.get(list); + } catch (IllegalArgumentException ex) { + return null; + } catch (IllegalAccessException ex) { + return null; + } + } + + public static List getRecipes() { + Field field = getField(CraftingManager.class, MineTweakerObfuscation.CRAFTINGMANAGER_RECIPES); + try { + return (List) field.get(CraftingManager.getInstance()); + } catch (IllegalAccessException ex) { + MineTweakerAPI.logger.logError("EPIC ERROR - could not load recipe list!"); + return null; + } + } + + public static HashMap> getOreStacks() { + try { + return (HashMap>) OREDICTIONARY_ORESTACKS.get(null); + } catch (IllegalAccessException ex) { + MineTweakerAPI.logger.logError("ERROR - could not load ore dictionary stacks!"); + return null; + } + } + + private static Field getField(Class cls, String[] names) { + for (String name : names) { + try { + Field field = cls.getField(name); + field.setAccessible(true); + return field; + } catch (NoSuchFieldException ex) { + } catch (SecurityException ex) { + } + } + + return null; + } +} diff --git a/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/util/MineTweakerObfuscation.java b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/util/MineTweakerObfuscation.java new file mode 100644 index 0000000..0f35719 --- /dev/null +++ b/MineTweaker3-MC17-Main/src/main/java/minetweaker/mc172/util/MineTweakerObfuscation.java @@ -0,0 +1,19 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package minetweaker.mc172.util; + +/** + * + * @author Stanneke + */ +public class MineTweakerObfuscation { + private MineTweakerObfuscation() {} + + public static final String[] NBTTAGLIST_TAGLIST = {"tagList", "field_74747_a"}; + public static final String[] CRAFTINGMANAGER_RECIPES = {"recipes", "field_77597_b"}; + public static final String[] OREDICTIONARY_ORESTACKS = {"oreStacks"}; +} diff --git a/MineTweaker3-MC17-Mod-IC2/build.gradle b/MineTweaker3-MC17-Mod-IC2/build.gradle new file mode 100644 index 0000000..73913f7 --- /dev/null +++ b/MineTweaker3-MC17-Mod-IC2/build.gradle @@ -0,0 +1,28 @@ +buildscript { + repositories { + mavenCentral() + maven { + name = "forge" + url = "http://files.minecraftforge.net/maven" + } + maven { + name = "sonatype" + url = "https://oss.sonatype.org/content/repositories/snapshots/" + } + } + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT' + } +} + +apply plugin: 'forge' +minecraft{version = "1.7.2-10.12.1.1112"} +version = "1.0" +archivesBaseName = project.projectDir.name + +dependencies { + compile project(':ZenScript') + compile project(':MineTweaker3-API') + compile project(':MineTweaker3-MC17-Main') + compile files('libs/industrialcraft-2-2.1.476-experimental-api.jar') +} diff --git a/MineTweaker3-MC17-Mod-IC2/libs/industrialcraft-2-2.1.476-experimental-api.jar b/MineTweaker3-MC17-Mod-IC2/libs/industrialcraft-2-2.1.476-experimental-api.jar new file mode 100644 index 0000000..22761c4 Binary files /dev/null and b/MineTweaker3-MC17-Mod-IC2/libs/industrialcraft-2-2.1.476-experimental-api.jar differ diff --git a/MineTweaker3-MC17-Mod-IC2/libs/industrialcraft-2-2.1.476-experimental-dev.jar b/MineTweaker3-MC17-Mod-IC2/libs/industrialcraft-2-2.1.476-experimental-dev.jar new file mode 100644 index 0000000..f098d8e Binary files /dev/null and b/MineTweaker3-MC17-Mod-IC2/libs/industrialcraft-2-2.1.476-experimental-dev.jar differ diff --git a/MineTweaker3-MC17-Mod-IC2/libs/industrialcraft-2-2.1.476-experimental.jar b/MineTweaker3-MC17-Mod-IC2/libs/industrialcraft-2-2.1.476-experimental.jar new file mode 100644 index 0000000..96e4500 Binary files /dev/null and b/MineTweaker3-MC17-Mod-IC2/libs/industrialcraft-2-2.1.476-experimental.jar differ diff --git a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Centrifuge.java b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Centrifuge.java new file mode 100644 index 0000000..fa54e49 --- /dev/null +++ b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Centrifuge.java @@ -0,0 +1,58 @@ +package stanhebben.minetweaker.mods.ic2; + +import ic2.api.recipe.Recipes; +import minetweaker.MineTweakerAPI; +import minetweaker.mc172.item.TweakerItemStack; +import minetweaker.minecraft.item.IIngredient; +import minetweaker.minecraft.item.IItemStack; +import net.minecraft.item.ItemStack; +import stanhebben.zenscript.annotations.NotNull; +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * Provides access to the IC2 compressor recipes. Recipes can be added but not + * removed, due to IC2 API restrictions. + * + * @author Stan Hebben + */ +@ZenClass("mods.ic2.Centrifuge") +public class Centrifuge { + /** + * Adds a new recipe to the centrifuge. + * + * The recipe input can be any pattern, as long as the stack size is determined. + * The output can be any item stack. + * + * @param output recipe output + * @param ingredient recipe input + */ + @ZenMethod + public static void addRecipe( + @NotNull IItemStack output, + @NotNull IIngredient ingredient) { + if (ingredient.getAmount() < 0) { + MineTweakerAPI.logger.logWarning("invalid ingredient: " + ingredient + " - stack size not known"); + } else { + MineTweakerAPI.tweaker.apply(new MachineAddRecipeAction( + "compressor", + Recipes.compressor, + (ItemStack) output.getInternal(), + null, + new IC2RecipeInput(ingredient))); + } + } + + /** + * Determines the recipe output for the given input. Will return the output + * of a single item, even if the stack contains multiple items. + * + * @param input recipe input + * @return recipe output + */ + @ZenMethod + public static IItemStack getOutput( + @NotNull IItemStack input) { + return new TweakerItemStack(Recipes.compressor.getOutputFor((ItemStack) input.getInternal(), false).items.get(0)); + } +} diff --git a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Compressor.java b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Compressor.java new file mode 100644 index 0000000..772b0fb --- /dev/null +++ b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Compressor.java @@ -0,0 +1,58 @@ +package stanhebben.minetweaker.mods.ic2; + +import ic2.api.recipe.Recipes; +import minetweaker.MineTweakerAPI; +import minetweaker.mc172.item.TweakerItemStack; +import minetweaker.minecraft.item.IIngredient; +import minetweaker.minecraft.item.IItemStack; +import net.minecraft.item.ItemStack; +import stanhebben.zenscript.annotations.NotNull; +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * Provides access to the IC2 compressor recipes. Recipes can be added but not + * removed, due to IC2 API restrictions. + * + * @author Stan Hebben + */ +@ZenClass("mods.ic2.Compressor") +public class Compressor { + /** + * Adds a new recipe to the compressor. + * + * The recipe input can be any pattern, as long as the stack size is determined. + * The output can be any item stack. + * + * @param output recipe output + * @param ingredient recipe input + */ + @ZenMethod + public static void addRecipe( + @NotNull IItemStack output, + @NotNull IIngredient ingredient) { + if (ingredient.getAmount() < 0) { + MineTweakerAPI.logger.logWarning("invalid ingredient: " + ingredient + " - stack size not known"); + } else { + MineTweakerAPI.tweaker.apply(new MachineAddRecipeAction( + "compressor", + Recipes.compressor, + (ItemStack) output.getInternal(), + null, + new IC2RecipeInput(ingredient))); + } + } + + /** + * Determines the recipe output for the given input. Will return the output + * of a single item, even if the stack contains multiple items. + * + * @param input recipe input + * @return recipe output + */ + @ZenMethod + public static IItemStack getOutput( + @NotNull IItemStack input) { + return new TweakerItemStack(Recipes.compressor.getOutputFor((ItemStack) input.getInternal(), false).items.get(0)); + } +} diff --git a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Extractor.java b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Extractor.java new file mode 100644 index 0000000..ec054be --- /dev/null +++ b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Extractor.java @@ -0,0 +1,63 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.minetweaker.mods.ic2; + +import ic2.api.recipe.Recipes; +import minetweaker.MineTweakerAPI; +import minetweaker.mc172.item.TweakerItemStack; +import minetweaker.minecraft.item.IIngredient; +import minetweaker.minecraft.item.IItemStack; +import net.minecraft.item.ItemStack; +import stanhebben.zenscript.annotations.NotNull; +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * Provides access to the IC2 Extractor recipes. + * + * @author Stan Hebben + */ +@ZenClass("mods.ic2.Extractor") +public class Extractor { + /** + * Adds a new recipe to the extractor. + * + * The recipe input can be any pattern, as long as the stack size is determined. + * The output can be any item stack. + * + * @param output recipe output + * @param ingredient recipe input + */ + @ZenMethod + public static void addRecipe( + @NotNull IItemStack output, + @NotNull IIngredient ingredient) { + if (ingredient.getAmount() < 0) { + MineTweakerAPI.logger.logWarning("invalid ingredient: " + ingredient + " - stack size not known"); + } else { + MineTweakerAPI.tweaker.apply(new MachineAddRecipeAction( + "extractor", + Recipes.extractor, + (ItemStack) output.getInternal(), + null, + new IC2RecipeInput(ingredient))); + } + } + + /** + * Determines the recipe output for the given input. Will return the output + * of a single item, even if the stack contains multiple items. + * + * @param input recipe input + * @return recipe output + */ + @ZenMethod + public static IItemStack getOutput( + @NotNull IItemStack input) { + return new TweakerItemStack(Recipes.extractor.getOutputFor((ItemStack) input.getInternal(), false).items.get(0)); + } +} diff --git a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/IC2RecipeInput.java b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/IC2RecipeInput.java new file mode 100644 index 0000000..6358164 --- /dev/null +++ b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/IC2RecipeInput.java @@ -0,0 +1,47 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.minetweaker.mods.ic2; + +import ic2.api.recipe.IRecipeInput; +import java.util.ArrayList; +import java.util.List; +import minetweaker.mc172.item.TweakerItemStack; +import minetweaker.minecraft.item.IIngredient; +import minetweaker.minecraft.item.IItemStack; +import net.minecraft.item.ItemStack; + +/** + * Wrapper class for ITweakerItemStackPatterns to IC2 recipe inputs. + * + * @author Stan Hebben + */ +public class IC2RecipeInput implements IRecipeInput { + private final IIngredient ingredient; + + public IC2RecipeInput(IIngredient ingredient) { + this.ingredient = ingredient; + } + + @Override + public boolean matches(ItemStack subject) { + return ingredient.matches(new TweakerItemStack(subject)); + } + + @Override + public int getAmount() { + return ingredient.getAmount(); + } + + @Override + public List getInputs() { + List items = new ArrayList(); + for (IItemStack item : ingredient.getItems()) { + items.add((ItemStack) item.getInternal()); + } + return items; + } +} diff --git a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Macerator.java b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Macerator.java new file mode 100644 index 0000000..8cf0b82 --- /dev/null +++ b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/Macerator.java @@ -0,0 +1,58 @@ +package stanhebben.minetweaker.mods.ic2; + +import ic2.api.recipe.Recipes; +import minetweaker.MineTweakerAPI; +import minetweaker.mc172.item.TweakerItemStack; +import minetweaker.minecraft.item.IIngredient; +import minetweaker.minecraft.item.IItemStack; +import net.minecraft.item.ItemStack; +import stanhebben.zenscript.annotations.NotNull; +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenMethod; + +/** + * Provides access to the IC2 macerator recipes. Recipes can be added but not + * removed, due to IC2 API restrictions. + * + * @author Stan Hebben + */ +@ZenClass("mods.ic2.Macerator") +public class Macerator { + /** + * Adds a new recipe to the macerator. + * + * The recipe input can be any pattern, as long as the stack size is determined. + * The output can be any item stack. + * + * @param output recipe output + * @param ingredient recipe input + */ + @ZenMethod + public static void addRecipe( + @NotNull IItemStack output, + @NotNull IIngredient ingredient) { + if (ingredient.getAmount() < 0) { + MineTweakerAPI.logger.logWarning("invalid ingredient: " + ingredient + " - stack size not known"); + } else { + MineTweakerAPI.tweaker.apply(new MachineAddRecipeAction( + "macerator", + Recipes.macerator, + (ItemStack) output.getInternal(), + null, + new IC2RecipeInput(ingredient))); + } + } + + /** + * Determines the recipe output for the given input. Will return the output + * of a single item, even if the stack contains multiple items. + * + * @param input recipe input + * @return recipe output + */ + @ZenMethod + public static IItemStack getOutput( + @NotNull IItemStack input) { + return new TweakerItemStack(Recipes.macerator.getOutputFor((ItemStack) input.getInternal(), false).items.get(0)); + } +} diff --git a/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/MachineAddRecipeAction.java b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/MachineAddRecipeAction.java new file mode 100644 index 0000000..1e95d21 --- /dev/null +++ b/MineTweaker3-MC17-Mod-IC2/src/main/java/stanhebben/minetweaker/mods/ic2/MachineAddRecipeAction.java @@ -0,0 +1,42 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.minetweaker.mods.ic2; + +import ic2.api.recipe.IMachineRecipeManager; +import minetweaker.OneWayAction; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +/** + * + * @author Stan + */ +public class MachineAddRecipeAction extends OneWayAction { + private final String name; + private final IMachineRecipeManager machine; + private final ItemStack output; + private final IC2RecipeInput input; + private final NBTTagCompound tag; + + public MachineAddRecipeAction(String name, IMachineRecipeManager machine, ItemStack output, NBTTagCompound tag, IC2RecipeInput input) { + this.name = name; + this.machine = machine; + this.output = output; + this.input = input; + this.tag = tag; + } + + @Override + public void apply() { + machine.addRecipe(input, tag, output); + } + + @Override + public String describe() { + return "Adding " + name + " recipe for " + output.getDisplayName(); + } +} diff --git a/MineTweaker3-MC17-Mod-NEI/build.gradle b/MineTweaker3-MC17-Mod-NEI/build.gradle new file mode 100644 index 0000000..2a3142a --- /dev/null +++ b/MineTweaker3-MC17-Mod-NEI/build.gradle @@ -0,0 +1,28 @@ +buildscript { + repositories { + mavenCentral() + maven { + name = "forge" + url = "http://files.minecraftforge.net/maven" + } + maven { + name = "sonatype" + url = "https://oss.sonatype.org/content/repositories/snapshots/" + } + } + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT' + } +} + +apply plugin: 'forge' +minecraft{version = "1.7.2-10.12.1.1112"} +version = "1.0" +archivesBaseName = project.projectDir.name + +dependencies { + compile project(':ZenScript') + compile project(':MineTweaker3-API') + compile project(':MineTweaker3-MC17-Main') + compile files('libs/NotEnoughItems-1.7.2-1.0.1-dev.jar') +} diff --git a/MineTweaker3-MC17-Mod-NEI/libs/NotEnoughItems-1.7.2-1.0.1-dev.jar b/MineTweaker3-MC17-Mod-NEI/libs/NotEnoughItems-1.7.2-1.0.1-dev.jar new file mode 100644 index 0000000..1e55129 Binary files /dev/null and b/MineTweaker3-MC17-Mod-NEI/libs/NotEnoughItems-1.7.2-1.0.1-dev.jar differ diff --git a/MineTweaker3-MC17-Mod-NEI/libs/NotEnoughItems-1.7.2-1.0.1-src.jar b/MineTweaker3-MC17-Mod-NEI/libs/NotEnoughItems-1.7.2-1.0.1-src.jar new file mode 100644 index 0000000..2e79644 Binary files /dev/null and b/MineTweaker3-MC17-Mod-NEI/libs/NotEnoughItems-1.7.2-1.0.1-src.jar differ diff --git a/MineTweaker3-MC17-Mod-NEI/src/main/java/stanhebben/minetweaker/mods/nei/NEI.java b/MineTweaker3-MC17-Mod-NEI/src/main/java/stanhebben/minetweaker/mods/nei/NEI.java new file mode 100644 index 0000000..ab88175 --- /dev/null +++ b/MineTweaker3-MC17-Mod-NEI/src/main/java/stanhebben/minetweaker/mods/nei/NEI.java @@ -0,0 +1,147 @@ +package stanhebben.minetweaker.mods.nei; + +import codechicken.nei.api.API; +import net.minecraft.item.ItemStack; +import stanhebben.zenscript.annotations.NotNull; +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenMethod; +import minetweaker.IUndoableAction; +import minetweaker.MineTweakerAPI; +import minetweaker.OneWayAction; +import minetweaker.minecraft.item.IItemStack; + +/** + * MineTweaker NEI support. + * + * Enables hiding NEI items as well as adding new item stacks. These item stacks + * can then show a custom message or contain NBT data. Can be used to show + * a custom message or lore with certain items, or to provide spawnable items + * with specific NBT tags. + * + * @author Stan Hebben + */ +@ZenClass("mods.nei.NEI") +public class NEI { + /** + * Hides a specific item in NEI. Will take into account metadata values, if + * any. + * + * @param item item to be hidden + */ + @ZenMethod + public static void hide(@NotNull IItemStack item) { + MineTweakerAPI.tweaker.apply(new NEIHideItemAction((ItemStack) item.getInternal())); + } + + /** + * Adds a NEI entry. The item stack can contain damage values and NBT tags. + * + * @param stack item stack to be added + */ + @ZenMethod + public static void addEntry(@NotNull IItemStack stack) { + MineTweakerAPI.tweaker.apply(new NEIAddEntryAction((ItemStack) stack.getInternal())); + } + + /** + * Overrides a name in NEI. Note that this will not change the original + * display name, but it will change the way it is displayed in NEI. + * + * @param item item + * @param name item name + */ + @ZenMethod + public static void overrideName(@NotNull IItemStack item, @NotNull String name) { + MineTweakerAPI.tweaker.apply(new NEIOverrideNameAction((ItemStack) item.getInternal(), name)); + } + + // ############################# + // ### Private inner classes ### + // ############################# + + private static class NEIAddEntryAction implements IUndoableAction { + private final ItemStack item; + + public NEIAddEntryAction(ItemStack item) { + this.item = item; + } + + @Override + public void apply() { + API.addItemListEntry(item); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + API.hideItem(item); + } + + @Override + public String describe() { + return "Adding " + item.getDisplayName() + " as NEI entry"; + } + + @Override + public String describeUndo() { + return "Removing " + item.getDisplayName() + " as NEI entry"; + } + } + + private static class NEIHideItemAction implements IUndoableAction { + private final ItemStack stack; + + public NEIHideItemAction(ItemStack stack) { + this.stack = stack; + } + + @Override + public void apply() { + API.hideItem(stack); + } + + @Override + public boolean canUndo() { + return true; + } + + @Override + public void undo() { + API.addItemListEntry(stack); + } + + @Override + public String describe() { + return "Hiding " + stack.getUnlocalizedName() + " in NEI"; + } + + @Override + public String describeUndo() { + return "Displaying " + stack.getUnlocalizedName() + " in NEI"; + } + } + + private static class NEIOverrideNameAction extends OneWayAction { + private final ItemStack item; + private final String name; + + public NEIOverrideNameAction(ItemStack item, String name) { + this.item = item; + this.name = name; + } + + @Override + public void apply() { + API.setOverrideName(item, name); + } + + @Override + public String describe() { + return "Overriding NEI item name of " + item.getUnlocalizedName() + " to " + name; + } + } +} diff --git a/ZenScript/build.gradle b/ZenScript/build.gradle new file mode 100644 index 0000000..d362f62 --- /dev/null +++ b/ZenScript/build.gradle @@ -0,0 +1,6 @@ +apply plugin: 'java' + +dependencies { + compile group: 'org.ow2.asm', name: 'asm', version:'4.2' + compile group: 'org.ow2.asm', name: 'asm-commons', version:'4.2' +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/IZenErrorLogger.java b/ZenScript/src/main/java/stanhebben/zenscript/IZenErrorLogger.java new file mode 100644 index 0000000..d57613b --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/IZenErrorLogger.java @@ -0,0 +1,19 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript; + +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public interface IZenErrorLogger { + public void error(ZenPosition position, String message); + + public void warning(ZenPosition position, String message); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/TypeExpansion.java b/ZenScript/src/main/java/stanhebben/zenscript/TypeExpansion.java new file mode 100644 index 0000000..a2eb48a --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/TypeExpansion.java @@ -0,0 +1,218 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.annotations.ZenCaster; +import stanhebben.zenscript.annotations.ZenGetter; +import stanhebben.zenscript.annotations.ZenMethod; +import stanhebben.zenscript.annotations.ZenMethodStatic; +import stanhebben.zenscript.annotations.ZenOperator; +import stanhebben.zenscript.annotations.ZenSetter; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.ITypeRegistry; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionString; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.expand.ZenExpandCaster; +import stanhebben.zenscript.type.expand.ZenExpandMember; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.type.natives.ZenNativeOperator; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class TypeExpansion { + private final Map members; + private final Map staticMembers; + private final List casters; + private final List trinaryOperators; + private final List binaryOperators; + private final List unaryOperators; + + public TypeExpansion() { + members = new HashMap(); + staticMembers = new HashMap(); + casters = new ArrayList(); + trinaryOperators = new ArrayList(); + binaryOperators = new ArrayList(); + unaryOperators = new ArrayList(); + } + + public void expand(Class cls, ITypeRegistry types) { + for (Method method : cls.getMethods()) { + String methodName = method.getName(); + + for (Annotation annotation : method.getAnnotations()) { + if (annotation instanceof ZenCaster) { + checkStatic(method); + casters.add(new ZenExpandCaster(new JavaMethod(cls, method, types))); + } else if (annotation instanceof ZenGetter) { + checkStatic(method); + ZenGetter getterAnnotation = (ZenGetter) annotation; + String name = getterAnnotation.value().length() == 0 ? method.getName() : getterAnnotation.value(); + + if (!members.containsKey(name)) { + members.put(name, new ZenExpandMember()); + } + members.get(name).setGetter(new JavaMethod(cls, method, types)); + } else if (annotation instanceof ZenSetter) { + checkStatic(method); + ZenSetter setterAnnotation = (ZenSetter) annotation; + String name = setterAnnotation.value().length() == 0 ? method.getName() : setterAnnotation.value(); + + if (!members.containsKey(name)) { + members.put(name, new ZenExpandMember()); + } + members.get(name).setSetter(new JavaMethod(cls, method, types)); + } else if (annotation instanceof ZenOperator) { + checkStatic(method); + ZenOperator operatorAnnotation = (ZenOperator) annotation; + switch (operatorAnnotation.value()) { + case NEG: + case NOT: + if (method.getParameterTypes().length != 0) { + // TODO: error + } else { + unaryOperators.add(new ZenNativeOperator( + operatorAnnotation.value(), + new JavaMethod(cls, method, types))); + } + break; + case ADD: + case SUB: + case CAT: + case MUL: + case DIV: + case MOD: + case AND: + case OR: + case XOR: + case INDEXGET: + case RANGE: + case CONTAINS: + case COMPARE: + if (method.getParameterTypes().length != 1) { + // TODO: error + } else { + binaryOperators.add(new ZenNativeOperator( + operatorAnnotation.value(), + new JavaMethod(cls, method, types))); + } + break; + case INDEXSET: + if (method.getParameterTypes().length != 2) { + // TODO: error + } else { + trinaryOperators.add(new ZenNativeOperator( + operatorAnnotation.value(), + new JavaMethod(cls, method, types))); + } + break; + } + } else if (annotation instanceof ZenMethod) { + checkStatic(method); + ZenMethod methodAnnotation = (ZenMethod) annotation; + if (methodAnnotation.value().length() > 0) { + methodName = methodAnnotation.value(); + } + if (!members.containsKey(methodName)) { + members.put(methodName, new ZenExpandMember()); + } + members.get(methodName).addMethod(new JavaMethod(cls, method, types)); + } else if (annotation instanceof ZenMethodStatic) { + checkStatic(method); + ZenMethodStatic methodAnnotation = (ZenMethodStatic) annotation; + if (methodAnnotation.value().length() > 0) { + methodName = methodAnnotation.value(); + } + if (!staticMembers.containsKey(methodName)) { + staticMembers.put(methodName, new ZenExpandMember()); + } + staticMembers.get(methodName).addMethod(new JavaMethod(cls, method, types)); + } + } + } + } + + public ZenExpandCaster getCaster(ZenType type, IEnvironmentGlobal environment) { + for (ZenExpandCaster caster : casters) { + if (caster.getTarget().equals(type)) { + return caster; + } + } + for (ZenExpandCaster caster : casters) { + if (caster.getTarget().canCastImplicit(type, environment)) { + return caster; + } + } + + return null; + } + + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + for (ZenNativeOperator op : unaryOperators) { + if (op.getOperator() == operator) { + return op.getMethod().callStatic(position, environment, value); + } + } + + return null; + } + + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + for (ZenNativeOperator op : binaryOperators) { + if (op.getOperator() == operator) { + return op.getMethod().callStatic(position, environment, left, right); + } + } + + return null; + } + + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + for (ZenNativeOperator op : trinaryOperators) { + if (op.getOperator() == operator) { + return op.getMethod().callStatic(position, environment, first, second, third); + } + } + + return null; + } + + public IPartialExpression member(ZenPosition position, IEnvironmentGlobal environment, Expression value, String member) { + if (members.containsKey(member)) { + return members.get(member).instance(position, environment, value); + } + + return null; + } + + public IPartialExpression staticMember(ZenPosition position, IEnvironmentGlobal environment, String member) { + if (staticMembers.containsKey(member)) { + return staticMembers.get(member).instance(position, environment); + } + + return null; + } + + private void checkStatic(Method method) { + if ((method.getModifiers() & Modifier.STATIC) == 0) { + throw new RuntimeException("Expansion method " + method.getName() + " must be static"); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/ZenModule.java b/ZenScript/src/main/java/stanhebben/zenscript/ZenModule.java new file mode 100644 index 0000000..90a4ebd --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/ZenModule.java @@ -0,0 +1,294 @@ +package stanhebben.zenscript; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import stanhebben.zenscript.compiler.ClassNameGenerator; +import stanhebben.zenscript.compiler.EnvironmentClass; +import stanhebben.zenscript.compiler.EnvironmentGlobal; +import stanhebben.zenscript.compiler.EnvironmentMethod; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.definitions.Import; +import stanhebben.zenscript.definitions.ParsedFunction; +import stanhebben.zenscript.definitions.ParsedFunctionArgument; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.statements.Statement; +import stanhebben.zenscript.statements.StatementReturn; +import stanhebben.zenscript.symbols.IZenCompileEnvironment; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.symbols.SymbolArgument; +import stanhebben.zenscript.symbols.SymbolZenStaticMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.StringUtil; +import static stanhebben.zenscript.util.ZenTypeUtil.internal; + +/** + * + * @author Stanneke + */ +public class ZenModule { + public static void compileScripts(String mainclass, String mainFileName, List scripts, IEnvironmentGlobal environmentGlobal) { + ClassWriter clsMain = new ClassWriter(ClassWriter.COMPUTE_MAXS); + clsMain.visitSource(mainclass, null); + + clsMain.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, "__ZenMain__", null, internal(Object.class), new String[] {internal(Runnable.class)}); + MethodOutput mainRun = new MethodOutput(clsMain, Opcodes.ACC_PUBLIC, "run", "()V", null, null); + mainRun.start(); + + for (ZenParsedFile script : scripts) { + for (Map.Entry function : script.getFunctions().entrySet()) { + ParsedFunction fn = function.getValue(); + environmentGlobal.putValue(function.getKey(), new SymbolZenStaticMethod( + script.getClassName(), + fn.getName(), + fn.getSignature(), + fn.getArgumentTypes(), + fn.getReturnType())); + } + } + + for (ZenParsedFile script : scripts) { + ClassWriter clsScript = new ClassWriter(ClassWriter.COMPUTE_MAXS); + clsScript.visitSource(script.getFileName(), null); + EnvironmentClass environmentScript = new EnvironmentClass(clsScript, environmentGlobal); + + clsScript.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, script.getClassName(), null, internal(Object.class), new String[] {internal(Runnable.class)}); + for (Import imprt : script.getImports()) { + List name = imprt.getName(); + IPartialExpression value = environmentGlobal.getValue(name.get(0), imprt.getPosition()); + for (int i = 1; i < name.size(); i++) { + IPartialExpression member = value.getMember(imprt.getPosition(), environmentGlobal, name.get(i)); + if (member == null) { + environmentGlobal.error(imprt.getPosition(), StringUtil.join(name, ".") + " not found"); + break; + } + value = member; + } + IZenSymbol symbol = value.toSymbol(); + if (symbol == null) { + environmentGlobal.error(imprt.getPosition(), StringUtil.join(name, ".") + " is not a valid import"); + } else { + environmentScript.putValue( + imprt.getRename() == null + ? name.get(name.size() - 1) + : imprt.getRename(), value.toSymbol()); + } + } + + for (Map.Entry function : script.getFunctions().entrySet()) { + ParsedFunction fn = function.getValue(); + + String signature = fn.getSignature(); + MethodOutput methodOutput = new MethodOutput(clsScript, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, function.getKey(), signature, null, null); + EnvironmentMethod methodEnvironment = new EnvironmentMethod(methodOutput, environmentScript); + + List arguments = function.getValue().getArguments(); + for (int i = 0; i < arguments.size(); i++) { + ParsedFunctionArgument argument = arguments.get(i); + methodEnvironment.putValue(argument.getName(), new SymbolArgument(i, argument.getType())); + } + + methodOutput.start(); + Statement[] statements = fn.getStatements(); + for (Statement statement : statements) { + statement.compile(methodEnvironment); + } + if (function.getValue().getReturnType() != ZenType.VOID) { + if (statements[statements.length - 1] instanceof StatementReturn) { + if (((StatementReturn) statements[statements.length - 1]).getExpression() != null) { + fn.getReturnType() + .defaultValue(fn.getPosition()) + .compile(true, methodEnvironment); + methodOutput.returnType(fn.getReturnType().toASMType()); + } + } else { + fn.getReturnType() + .defaultValue(fn.getPosition()) + .compile(true, methodEnvironment); + methodOutput.returnType(fn.getReturnType().toASMType()); + } + } + methodOutput.end(); + } + + if (script.getStatements().size() > 0) { + MethodOutput scriptOutput = new MethodOutput(clsScript, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "__script__", "()V", null, null); + IEnvironmentMethod functionMethod = new EnvironmentMethod(scriptOutput, environmentScript); + scriptOutput.start(); + for (Statement statement : script.getStatements()) { + statement.compile(functionMethod); + } + scriptOutput.ret(); + scriptOutput.end(); + + mainRun.invokeStatic(script.getClassName(), "__script__", "()V"); + } + + clsScript.visitEnd(); + environmentGlobal.putClass(script.getClassName(), clsScript.toByteArray()); + } + + mainRun.ret(); + mainRun.end(); + clsMain.visitEnd(); + + MethodVisitor constructor = clsMain.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null); + constructor.visitCode(); + constructor.visitVarInsn(Opcodes.ALOAD, 0); + constructor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V"); + constructor.visitInsn(Opcodes.RETURN); + constructor.visitMaxs(0, 0); + constructor.visitEnd(); + + environmentGlobal.putClass("__ZenMain__", clsMain.toByteArray()); + } + + public static ZenModule compileScriptFile(File single, IZenCompileEnvironment environment) throws IOException { + Map classes = new HashMap(); + ClassNameGenerator nameGen = new ClassNameGenerator(); + EnvironmentGlobal environmentGlobal = new EnvironmentGlobal( + environment, + classes, + nameGen + ); + + String filename = single.getName(); + String className = extractClassName(filename); + + FileInputStream input = new FileInputStream(single); + Reader reader = new InputStreamReader(new BufferedInputStream(input)); + ZenParser parser = new ZenParser(reader, environment); + ZenParsedFile file = new ZenParsedFile(filename, className, parser, environmentGlobal); + reader.close(); + + List files = new ArrayList(); + files.add(file); + + compileScripts(className, filename, files, environmentGlobal); + + // debug: output classes + File outputDir = new File("generated"); + outputDir.mkdir(); + + for (Map.Entry entry : classes.entrySet()) { + File outputFile = new File(outputDir, entry.getKey() + ".class"); + FileOutputStream output = new FileOutputStream(outputFile); + output.write(entry.getValue()); + output.close(); + } + + return new ZenModule(classes); + } + + public static ZenModule compileZip(File file, String subdir, IZenCompileEnvironment environment) throws IOException { + Map classes = new HashMap(); + ClassNameGenerator nameGen = new ClassNameGenerator(); + EnvironmentGlobal environmentGlobal = new EnvironmentGlobal( + environment, + classes, + nameGen + ); + + List files = new ArrayList(); + + ZipFile zipFile = new ZipFile(file); + Enumeration entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + + if (entry.getName().startsWith(subdir) && !entry.getName().equals(subdir)) { + String filename = entry.getName().substring(subdir.length()); + String className = extractClassName(filename); + + Reader reader = new InputStreamReader(new BufferedInputStream(zipFile.getInputStream(entry))); + ZenParser parser = new ZenParser(reader, environment); + ZenParsedFile pfile = new ZenParsedFile(filename, className, parser, environmentGlobal); + files.add(pfile); + reader.close(); + } + } + + String filename = file.getName(); + String className = extractClassName(filename); + compileScripts(className, filename, files, environmentGlobal); + + // debug: output classes + File outputDir = new File("generated"); + outputDir.mkdir(); + + for (Map.Entry entry : classes.entrySet()) { + File outputFile = new File(outputDir, entry.getKey() + ".class"); + FileOutputStream output = new FileOutputStream(outputFile); + output.write(entry.getValue()); + output.close(); + } + + return new ZenModule(classes); + } + + private final Map classes; + private final MyClassLoader classLoader; + + private ZenModule(Map classes) { + this.classes = classes; + classLoader = new MyClassLoader(); + } + + public Runnable getMain() { + try { + return (Runnable) classLoader.loadClass("__ZenMain__").newInstance(); + } catch (InstantiationException e) { + return null; + } catch (IllegalAccessException e) { + return null; + } catch (ClassNotFoundException ex) { + return null; + } + } + + private class MyClassLoader extends ClassLoader { + @Override + public Class findClass(String name) throws ClassNotFoundException { + if (classes.containsKey(name)) { + return this.defineClass(name, classes.get(name), 0, classes.get(name).length); + } + + return super.findClass(name); + } + } + + private static String extractClassName(String filename) { + filename = filename.replace('\\', '/'); + if (filename.startsWith("/")) filename = filename.substring(1); + + int lastSlash = filename.lastIndexOf('/'); + int lastDot = filename.lastIndexOf('.'); + + if (lastDot > lastSlash) { + filename = filename.substring(0, lastDot); + } + if (lastSlash > 0) { + String dir = filename.substring(0, lastSlash); + String name = filename.substring(lastSlash + 1, lastSlash + 2).toUpperCase() + filename.substring(lastSlash + 2); + return dir + '.' + name; + } else { + return filename.substring(0, 1).toUpperCase() + filename.substring(1); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/ZenParsedFile.java b/ZenScript/src/main/java/stanhebben/zenscript/ZenParsedFile.java new file mode 100644 index 0000000..8db60d6 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/ZenParsedFile.java @@ -0,0 +1,97 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import static stanhebben.zenscript.ZenParser.*; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.definitions.Import; +import stanhebben.zenscript.definitions.ParsedFunction; +import stanhebben.zenscript.parser.Token; +import stanhebben.zenscript.statements.Statement; + +/** + * + * @author Stanneke + */ +public class ZenParsedFile { + private final String filename; + private final String classname; + private final List imports; + private final Map functions; + private final List statements; + + public ZenParsedFile(String filename, String classname, ZenParser parser, IEnvironmentGlobal environment) { + this.filename = filename; + this.classname = classname; + + imports = new ArrayList(); + functions = new HashMap(); + statements = new ArrayList(); + + parser.setFile(this); + + while (parser.peek() != null && parser.peek().getType() == T_IMPORT) { + Token start = parser.next(); + + List importName = new ArrayList(); + Token tName = parser.required(T_ID, "identifier expected"); + importName.add(tName.getValue()); + + while (parser.optional(T_DOT) != null) { + Token tNamePart = parser.required(T_ID, "identifier expected"); + importName.add(tNamePart.getValue()); + } + + String rename = null; + if (parser.optional(T_AS) != null) { + Token tRename = parser.required(T_ID, "identifier expected"); + rename = tRename.getValue(); + } + + parser.required(T_SEMICOLON, "; expected"); + + imports.add(new Import(start.getPosition(), importName, rename)); + } + + while (parser.hasNext()) { + Token next = parser.peek(); + if (next.getType() == T_FUNCTION) { + ParsedFunction function = ParsedFunction.parse(parser, environment); + if (functions.containsKey(function.getName())) { + environment.error(function.getPosition(), "function " + function.getName() + " already exists"); + } + functions.put(function.getName(), function); + } else { + statements.add(Statement.read(parser, environment)); + } + } + } + + public String getClassName() { + return classname; + } + + public String getFileName() { + return filename; + } + + public List getImports() { + return imports; + } + + public List getStatements() { + return statements; + } + + public Map getFunctions() { + return functions; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/ZenParsedModule.java b/ZenScript/src/main/java/stanhebben/zenscript/ZenParsedModule.java new file mode 100644 index 0000000..a248860 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/ZenParsedModule.java @@ -0,0 +1,17 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript; + +import java.util.List; + +/** + * + * @author Stanneke + */ +public class ZenParsedModule { + private List files; +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/ZenParser.java b/ZenScript/src/main/java/stanhebben/zenscript/ZenParser.java new file mode 100644 index 0000000..cfc4e28 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/ZenParser.java @@ -0,0 +1,250 @@ +package stanhebben.zenscript; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.HashMap; +import stanhebben.zenscript.parser.CompiledDFA; +import stanhebben.zenscript.parser.NFA; +import stanhebben.zenscript.parser.Token; +import stanhebben.zenscript.parser.TokenStream; +import stanhebben.zenscript.symbols.IZenCompileEnvironment; + +public class ZenParser extends TokenStream { + private static final HashMap KEYWORDS; + + public static final int T_ID = 1; + public static final int T_INTVALUE = 2; + public static final int T_FLOATVALUE = 3; + public static final int T_STRINGVALUE = 4; + public static final int T_AOPEN = 5; + public static final int T_ACLOSE = 6; + public static final int T_SQBROPEN = 7; + public static final int T_SQBRCLOSE = 8; + public static final int T_DOT2 = 9; + public static final int T_DOT = 10; + public static final int T_COMMA = 11; + public static final int T_PLUSASSIGN = 12; + public static final int T_PLUS = 13; + public static final int T_MINUSASSIGN = 14; + public static final int T_MINUS = 15; + public static final int T_MULASSIGN = 16; + public static final int T_MUL = 17; + public static final int T_DIVASSIGN = 18; + public static final int T_DIV = 19; + public static final int T_MODASSIGN = 20; + public static final int T_MOD = 21; + public static final int T_ORASSIGN = 22; + public static final int T_OR = 23; + public static final int T_OR2 = 40; + public static final int T_ANDASSIGN = 24; + public static final int T_AND2 = 41; + public static final int T_AND = 25; + public static final int T_XORASSIGN = 26; + public static final int T_XOR = 27; + public static final int T_QUEST = 28; + public static final int T_COLON = 29; + public static final int T_BROPEN = 30; + public static final int T_BRCLOSE = 31; + public static final int T_TILDEASSIGN = 45; + public static final int T_TILDE = 32; + public static final int T_SEMICOLON = 33; + public static final int T_LTEQ = 34; + public static final int T_LT = 35; + public static final int T_GTEQ = 36; + public static final int T_GT = 37; + public static final int T_EQ = 38; + public static final int T_ASSIGN = 39; + public static final int T_NOTEQ = 42; + public static final int T_NOT = 43; + public static final int T_DOLLAR = 44; + + public static final int T_ANY = 99; + public static final int T_BOOL = 100; + public static final int T_BYTE = 101; + public static final int T_SHORT = 102; + public static final int T_INT = 103; + public static final int T_LONG = 104; + public static final int T_FLOAT = 105; + public static final int T_DOUBLE = 106; + public static final int T_STRING = 107; + public static final int T_FUNCTION = 108; + public static final int T_IN = 109; + public static final int T_VOID = 110; + + public static final int T_AS = 120; + public static final int T_VERSION = 121; + public static final int T_IF = 122; + public static final int T_ELSE = 123; + public static final int T_FOR = 124; + public static final int T_RETURN = 125; + public static final int T_VAR = 126; + public static final int T_VAL = 127; + + public static final int T_NULL = 140; + public static final int T_TRUE = 141; + public static final int T_FALSE = 142; + + public static final int T_IMPORT = 160; + + private static final String[] REGEXPS = { + "#[^\n]*\n", + "//[^\n]*\n", + "[ \t\r\n]*", + "[a-zA-Z_][a-zA-Z_0-9]*", + "\\-?(0|[1-9][0-9]*)\\.[0-9]+([eE][\\+\\-]?[0-9]+)?", + "\\-?(0|[1-9][0-9]*)", + "\"([^\"\\\\]|\\\\([\"\\\\/bfnrt]|u[0-9a-fA-F]{4}))*\"", + "\\{", + "\\}", + "\\[", + "\\]", + "\\.\\.", + "\\.", + ",", + "\\+=", + "\\+", + "\\-=", + "\\-", + "\\*=", + "\\*", + "/=", + "/", + "%=", + "%", + "\\|=", + "\\|\\|", + "\\|", + "&=", + "&&", + "&", + "\\^=", + "\\^", + "\\?", + ":", + "\\(", + "\\)", + "~=", + "~", + ";", + "<=", + "<", + ">=", + ">", + "==", + "=", + "!=", + "!", + "$" + }; + private static final int[] FINALS = { + -1, + -1, + -1, + T_ID, + T_FLOATVALUE, + T_INTVALUE, + T_STRINGVALUE, + T_AOPEN, + T_ACLOSE, + T_SQBROPEN, + T_SQBRCLOSE, + T_DOT2, + T_DOT, + T_COMMA, + T_PLUSASSIGN, + T_PLUS, + T_MINUSASSIGN, + T_MINUS, + T_MULASSIGN, + T_MUL, + T_DIVASSIGN, + T_DIV, + T_MODASSIGN, + T_MOD, + T_ORASSIGN, + T_OR2, + T_OR, + T_ANDASSIGN, + T_AND2, + T_AND, + T_XORASSIGN, + T_XOR, + T_QUEST, + T_COLON, + T_BROPEN, + T_BRCLOSE, + T_TILDEASSIGN, + T_TILDE, + T_SEMICOLON, + T_LTEQ, + T_LT, + T_GTEQ, + T_GT, + T_EQ, + T_ASSIGN, + T_NOTEQ, + T_NOT, + T_DOLLAR + }; + private static final CompiledDFA DFA = new NFA(REGEXPS, FINALS).toDFA().optimize().compile(); + + static { + KEYWORDS = new HashMap(); + KEYWORDS.put("any", T_ANY); + KEYWORDS.put("bool", T_BOOL); + KEYWORDS.put("byte", T_BYTE); + KEYWORDS.put("short", T_SHORT); + KEYWORDS.put("int", T_INT); + KEYWORDS.put("long", T_LONG); + KEYWORDS.put("float", T_FLOAT); + KEYWORDS.put("double", T_DOUBLE); + KEYWORDS.put("string", T_STRING); + KEYWORDS.put("function", T_FUNCTION); + KEYWORDS.put("in", T_IN); + KEYWORDS.put("void", T_VOID); + + KEYWORDS.put("as", T_AS); + KEYWORDS.put("version", T_VERSION); + KEYWORDS.put("if", T_IF); + KEYWORDS.put("else", T_ELSE); + KEYWORDS.put("for", T_FOR); + KEYWORDS.put("return", T_RETURN); + KEYWORDS.put("var", T_VAR); + KEYWORDS.put("val", T_VAL); + + KEYWORDS.put("null", T_NULL); + KEYWORDS.put("true", T_TRUE); + KEYWORDS.put("false", T_FALSE); + + KEYWORDS.put("import", T_IMPORT); + } + + private final IZenCompileEnvironment environment; + + public ZenParser(Reader contents, IZenCompileEnvironment environment) throws IOException { + super(contents, DFA); + + this.environment = environment; + } + + public ZenParser(String contents, IZenCompileEnvironment environment) throws IOException { + super(new StringReader(contents), DFA); + + this.environment = environment; + } + + public IZenCompileEnvironment getEnvironment() { + return environment; + } + + @Override + public Token process(Token token) { + if (token.getType() == T_ID) { + if (KEYWORDS.containsKey(token.getValue())) { + return new Token(token.getValue(), KEYWORDS.get(token.getValue()), token.getPosition()); + } + } + return token; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/ZenRuntimeException.java b/ZenScript/src/main/java/stanhebben/zenscript/ZenRuntimeException.java new file mode 100644 index 0000000..2fdfa09 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/ZenRuntimeException.java @@ -0,0 +1,17 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript; + +/** + * + * @author Stanneke + */ +public class ZenRuntimeException extends RuntimeException { + public ZenRuntimeException(String message) { + super(message); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/annotations/CompareType.java b/ZenScript/src/main/java/stanhebben/zenscript/annotations/CompareType.java new file mode 100644 index 0000000..525581e --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/annotations/CompareType.java @@ -0,0 +1,21 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +/** + * + * @author Stanneke + */ +public enum CompareType { + FULL, + LT, + GT, + EQ, + NE, + LE, + GE +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/annotations/IterableList.java b/ZenScript/src/main/java/stanhebben/zenscript/annotations/IterableList.java new file mode 100644 index 0000000..41a20bd --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/annotations/IterableList.java @@ -0,0 +1,23 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Makes a class implementing List expose itself as iterable over keys and values. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface IterableList { + String value(); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/annotations/IterableMap.java b/ZenScript/src/main/java/stanhebben/zenscript/annotations/IterableMap.java new file mode 100644 index 0000000..1a4c15a --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/annotations/IterableMap.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Makes a class implementing Map expose itself as iterable over keys and values. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface IterableMap { + String key(); + String value(); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/annotations/IterableSimple.java b/ZenScript/src/main/java/stanhebben/zenscript/annotations/IterableSimple.java new file mode 100644 index 0000000..3930590 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/annotations/IterableSimple.java @@ -0,0 +1,23 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates a simple iterable. Should implement the iterable interface. + * + * @author Stanneke + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface IterableSimple { + String value(); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/annotations/NotNull.java b/ZenScript/src/main/java/stanhebben/zenscript/annotations/NotNull.java new file mode 100644 index 0000000..d266814 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/annotations/NotNull.java @@ -0,0 +1,22 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Stanneke + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface NotNull { + +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/annotations/OperatorType.java b/ZenScript/src/main/java/stanhebben/zenscript/annotations/OperatorType.java new file mode 100644 index 0000000..d8217fd --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/annotations/OperatorType.java @@ -0,0 +1,33 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +/** + * + * @author Stanneke + */ +public enum OperatorType { + ADD, + SUB, + MUL, + DIV, + MOD, + CAT, + OR, + AND, + XOR, + NEG, + NOT, + INDEXSET, + INDEXGET, + RANGE, + CONTAINS, + COMPARE, + MEMBERGETTER, + MEMBERSETTER, + EQUALS +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/annotations/Optional.java b/ZenScript/src/main/java/stanhebben/zenscript/annotations/Optional.java new file mode 100644 index 0000000..3230126 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/annotations/Optional.java @@ -0,0 +1,25 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Denotes that this parameter is optional. Inside the script, this parameter + * may be omitted, in which case it is automatically filled with the default + * value of that parameter type (false, null or 0). Invalid for @NonNull type. + * + * @author Stanneke + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Optional { + +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenCaster.java b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenCaster.java new file mode 100644 index 0000000..ce88f47 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenCaster.java @@ -0,0 +1,23 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Denotes a caster. Casters are capable of converting one type into another. + * They implement the 'as' operator. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ZenCaster { +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenClass.java b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenClass.java new file mode 100644 index 0000000..1f14713 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenClass.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Exposes this class to ZenScript. The class can then be imported and accessed + * into any script. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface ZenClass { + String value() default ""; +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenExpansion.java b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenExpansion.java new file mode 100644 index 0000000..bf7abf0 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenExpansion.java @@ -0,0 +1,22 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Stanneke + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface ZenExpansion { + public String value(); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenGetter.java b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenGetter.java new file mode 100644 index 0000000..31e6530 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenGetter.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates a getter. Will make the given method accessible as getter, that is, + * it's called when value.name is being used. + * + * @author Stan Hebben + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ZenGetter { + String value() default ""; +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenMemberGetter.java b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenMemberGetter.java new file mode 100644 index 0000000..c548e9a --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenMemberGetter.java @@ -0,0 +1,22 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Stan + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ZenMemberGetter { + +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenMemberSetter.java b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenMemberSetter.java new file mode 100644 index 0000000..fe64cf9 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenMemberSetter.java @@ -0,0 +1,22 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Stan + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ZenMemberSetter { + +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenMethod.java b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenMethod.java new file mode 100644 index 0000000..d8732d1 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenMethod.java @@ -0,0 +1,22 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Stanneke + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ZenMethod { + public String value() default ""; +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenMethodStatic.java b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenMethodStatic.java new file mode 100644 index 0000000..01c3ac4 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenMethodStatic.java @@ -0,0 +1,22 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Stanneke + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ZenMethodStatic { + public String value() default ""; +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenOperator.java b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenOperator.java new file mode 100644 index 0000000..91b5b03 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenOperator.java @@ -0,0 +1,22 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Stanneke + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ZenOperator { + OperatorType value(); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenSetter.java b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenSetter.java new file mode 100644 index 0000000..d8b37bc --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/annotations/ZenSetter.java @@ -0,0 +1,22 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Stanneke + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ZenSetter { + String value() default ""; +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/compiler/ClassNameGenerator.java b/ZenScript/src/main/java/stanhebben/zenscript/compiler/ClassNameGenerator.java new file mode 100644 index 0000000..7252b00 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/compiler/ClassNameGenerator.java @@ -0,0 +1,27 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.compiler; + +/** + * + * @author Stanneke + */ +public class ClassNameGenerator { + private int counter = 0; + + public ClassNameGenerator() { + + } + + public String generate() { + return "ZenClass" + counter++; + } + + public String generate(String prefix) { + return prefix + (counter++); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentClass.java b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentClass.java new file mode 100644 index 0000000..900b73f --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentClass.java @@ -0,0 +1,93 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.compiler; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import org.objectweb.asm.ClassVisitor; +import stanhebben.zenscript.TypeExpansion; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.symbols.IZenCompileEnvironment; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +public class EnvironmentClass implements IEnvironmentClass { + private final ClassVisitor output; + private final IEnvironmentGlobal global; + private final Map local; + + public EnvironmentClass(ClassVisitor output, IEnvironmentGlobal global) { + this.output = output; + this.global = global; + this.local = new HashMap(); + } + + @Override + public ClassVisitor getClassOutput() { + return output; + } + + @Override + public ZenType getType(Type type) { + return global.getType(type); + } + + @Override + public IZenCompileEnvironment getEnvironment() { + return global.getEnvironment(); + } + + @Override + public TypeExpansion getExpansion(String name) { + return global.getExpansion(name); + } + + @Override + public String makeClassName() { + return global.makeClassName(); + } + + @Override + public boolean containsClass(String name) { + return global.containsClass(name); + } + + @Override + public void putClass(String name, byte[] data) { + global.putClass(name, data); + } + + @Override + public IPartialExpression getValue(String name, ZenPosition position) { + if (local.containsKey(name)) { + return local.get(name).instance(position); + } else { + return global.getValue(name, position); + } + } + + @Override + public void putValue(String name, IZenSymbol value) { + local.put(name, value); + } + + @Override + public void error(ZenPosition position, String message) { + global.error(position, message); + } + + @Override + public void warning(ZenPosition position, String message) { + global.warning(position, message); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentGlobal.java b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentGlobal.java new file mode 100644 index 0000000..b6fac0b --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentGlobal.java @@ -0,0 +1,106 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.compiler; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import stanhebben.zenscript.IZenErrorLogger; +import stanhebben.zenscript.TypeExpansion; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.symbols.IZenCompileEnvironment; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class EnvironmentGlobal implements IEnvironmentGlobal { + private final IZenCompileEnvironment environment; + private final IZenErrorLogger errors; + private final Map classes; + private final Map local; + private final ClassNameGenerator nameGen; + private final TypeRegistry types; + + public EnvironmentGlobal( + IZenCompileEnvironment environment, + Map classes, + ClassNameGenerator nameGen) { + this.environment = environment; + this.errors = environment.getErrorLogger(); + this.classes = classes; + this.nameGen = nameGen; + this.types = environment.getTypeRegistry(); + this.local = new HashMap(); + } + + public IZenCompileEnvironment getCompileEnvironment() { + return environment; + } + + @Override + public ZenType getType(Type type) { + return types.getType(type); + } + + @Override + public boolean containsClass(String name) { + return classes.containsKey(name); + } + + @Override + public void putClass(String name, byte[] data) { + classes.put(name, data); + } + + @Override + public String makeClassName() { + return nameGen.generate(); + } + + @Override + public TypeExpansion getExpansion(String type) { + return environment.getExpansion(type); + } + + @Override + public void error(ZenPosition position, String message) { + errors.error(position, message); + } + + @Override + public void warning(ZenPosition position, String message) { + errors.warning(position, message); + } + + @Override + public IZenCompileEnvironment getEnvironment() { + return environment; + } + + @Override + public IPartialExpression getValue(String name, ZenPosition position) { + if (local.containsKey(name)) { + return local.get(name).instance(position); + } else { + IZenSymbol symbol = environment.getGlobal(name); + return symbol == null ? null : symbol.instance(position); + } + } + + @Override + public void putValue(String name, IZenSymbol value) { + if (local.containsKey(name)) { + throw new RuntimeException("already exists"); + } else { + local.put(name, value); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentMethod.java b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentMethod.java new file mode 100644 index 0000000..ec0c90d --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentMethod.java @@ -0,0 +1,110 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.compiler; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import org.objectweb.asm.ClassVisitor; +import stanhebben.zenscript.TypeExpansion; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.symbols.IZenCompileEnvironment; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.symbols.SymbolLocal; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class EnvironmentMethod implements IEnvironmentMethod { + private final MethodOutput output; + private final HashMap locals; + private final IEnvironmentClass environment; + private final Map local; + + public EnvironmentMethod(MethodOutput output, IEnvironmentClass environment) { + this.output = output; + this.locals = new HashMap(); + this.environment = environment; + this.local = new HashMap(); + } + + @Override + public MethodOutput getOutput() { + return output; + } + + @Override + public ClassVisitor getClassOutput() { + return environment.getClassOutput(); + } + + @Override + public int getLocal(SymbolLocal variable) { + if (!locals.containsKey(variable)) { + locals.put(variable, output.local(variable.getType().toASMType())); + } + return locals.get(variable); + } + + @Override + public ZenType getType(Type type) { + return environment.getType(type); + } + + @Override + public void error(ZenPosition position, String message) { + environment.error(position, message); + } + + @Override + public void warning(ZenPosition position, String message) { + environment.warning(position, message); + } + + @Override + public IZenCompileEnvironment getEnvironment() { + return environment.getEnvironment(); + } + + @Override + public TypeExpansion getExpansion(String name) { + return environment.getExpansion(name); + } + + @Override + public String makeClassName() { + return environment.makeClassName(); + } + + @Override + public void putClass(String name, byte[] data) { + environment.putClass(name, data); + } + + @Override + public boolean containsClass(String name) { + return environment.containsClass(name); + } + + @Override + public IPartialExpression getValue(String name, ZenPosition position) { + if (local.containsKey(name)) { + return local.get(name).instance(position); + } else { + return environment.getValue(name, position); + } + } + + @Override + public void putValue(String name, IZenSymbol value) { + local.put(name, value); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentScope.java b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentScope.java new file mode 100644 index 0000000..c7b5c52 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/compiler/EnvironmentScope.java @@ -0,0 +1,109 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.compiler; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import org.objectweb.asm.ClassVisitor; +import stanhebben.zenscript.TypeExpansion; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.symbols.IZenCompileEnvironment; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.symbols.SymbolLocal; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +public class EnvironmentScope implements IEnvironmentMethod { + private final IEnvironmentMethod outer; + private final Map local; + private final Map locals; + + public EnvironmentScope(IEnvironmentMethod outer) { + this.outer = outer; + this.local = new HashMap(); + this.locals = new HashMap(); + } + + @Override + public MethodOutput getOutput() { + return outer.getOutput(); + } + + @Override + public int getLocal(SymbolLocal variable) { + if (locals.containsKey(variable)) { + return locals.get(variable); + } else { + return outer.getLocal(variable); + } + } + + @Override + public ClassVisitor getClassOutput() { + return outer.getClassOutput(); + } + + @Override + public ZenType getType(Type type) { + return outer.getType(type); + } + + @Override + public IZenCompileEnvironment getEnvironment() { + return outer.getEnvironment(); + } + + @Override + public TypeExpansion getExpansion(String name) { + return outer.getExpansion(name); + } + + @Override + public String makeClassName() { + return outer.makeClassName(); + } + + @Override + public boolean containsClass(String name) { + return outer.containsClass(name); + } + + @Override + public void putClass(String name, byte[] data) { + outer.putClass(name, data); + } + + @Override + public IPartialExpression getValue(String name, ZenPosition position) { + if (local.containsKey(name)) { + return local.get(name).instance(position); + } else { + return outer.getValue(name, position); + } + } + + @Override + public void putValue(String name, IZenSymbol value) { + local.put(name, value); + } + + @Override + public void error(ZenPosition position, String message) { + outer.error(position, message); + } + + @Override + public void warning(ZenPosition position, String message) { + outer.warning(position, message); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/compiler/IEnvironmentClass.java b/ZenScript/src/main/java/stanhebben/zenscript/compiler/IEnvironmentClass.java new file mode 100644 index 0000000..bc7a9c1 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/compiler/IEnvironmentClass.java @@ -0,0 +1,17 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.compiler; + +import org.objectweb.asm.ClassVisitor; + +/** + * + * @author Stan + */ +public interface IEnvironmentClass extends IEnvironmentGlobal { + public ClassVisitor getClassOutput(); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/compiler/IEnvironmentGlobal.java b/ZenScript/src/main/java/stanhebben/zenscript/compiler/IEnvironmentGlobal.java new file mode 100644 index 0000000..e05f2e2 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/compiler/IEnvironmentGlobal.java @@ -0,0 +1,36 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.compiler; + +import java.lang.reflect.Type; +import stanhebben.zenscript.IZenErrorLogger; +import stanhebben.zenscript.TypeExpansion; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.symbols.IZenCompileEnvironment; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +public interface IEnvironmentGlobal extends ITypeRegistry, IZenErrorLogger { + public IZenCompileEnvironment getEnvironment(); + + public TypeExpansion getExpansion(String name); + + public String makeClassName(); + + public boolean containsClass(String name); + + public void putClass(String name, byte[] data); + + public IPartialExpression getValue(String name, ZenPosition position); + + public void putValue(String name, IZenSymbol value); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/compiler/IEnvironmentMethod.java b/ZenScript/src/main/java/stanhebben/zenscript/compiler/IEnvironmentMethod.java new file mode 100644 index 0000000..158a4a9 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/compiler/IEnvironmentMethod.java @@ -0,0 +1,20 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.compiler; + +import stanhebben.zenscript.symbols.SymbolLocal; +import stanhebben.zenscript.util.MethodOutput; + +/** + * + * @author Stan + */ +public interface IEnvironmentMethod extends IEnvironmentClass { + public MethodOutput getOutput(); + + public int getLocal(SymbolLocal variable); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/compiler/ITypeRegistry.java b/ZenScript/src/main/java/stanhebben/zenscript/compiler/ITypeRegistry.java new file mode 100644 index 0000000..155ed0f --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/compiler/ITypeRegistry.java @@ -0,0 +1,18 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.compiler; + +import java.lang.reflect.Type; +import stanhebben.zenscript.type.ZenType; + +/** + * + * @author Stan + */ +public interface ITypeRegistry { + public ZenType getType(Type type); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/compiler/TypeRegistry.java b/ZenScript/src/main/java/stanhebben/zenscript/compiler/TypeRegistry.java new file mode 100644 index 0000000..6095abf --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/compiler/TypeRegistry.java @@ -0,0 +1,126 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.compiler; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeAny; +import stanhebben.zenscript.type.ZenTypeArrayBasic; +import stanhebben.zenscript.type.ZenTypeArrayList; +import stanhebben.zenscript.type.ZenTypeAssociative; +import stanhebben.zenscript.type.ZenTypeBool; +import stanhebben.zenscript.type.ZenTypeBoolObject; +import stanhebben.zenscript.type.ZenTypeByte; +import stanhebben.zenscript.type.ZenTypeByteObject; +import stanhebben.zenscript.type.ZenTypeDouble; +import stanhebben.zenscript.type.ZenTypeDoubleObject; +import stanhebben.zenscript.type.ZenTypeFloat; +import stanhebben.zenscript.type.ZenTypeFloatObject; +import stanhebben.zenscript.type.ZenTypeInt; +import stanhebben.zenscript.type.ZenTypeIntObject; +import stanhebben.zenscript.type.ZenTypeLong; +import stanhebben.zenscript.type.ZenTypeLongObject; +import stanhebben.zenscript.type.ZenTypeNative; +import stanhebben.zenscript.type.ZenTypeShort; +import stanhebben.zenscript.type.ZenTypeShortObject; +import stanhebben.zenscript.type.ZenTypeString; +import stanhebben.zenscript.type.ZenTypeVoid; + +/** + * + * @author Stanneke + */ +public class TypeRegistry implements ITypeRegistry { + private final Map types; + + public TypeRegistry() { + types = new HashMap(); + + types.put(boolean.class, ZenTypeBool.INSTANCE); + types.put(byte.class, ZenTypeByte.INSTANCE); + types.put(short.class, ZenTypeShort.INSTANCE); + types.put(int.class, ZenTypeInt.INSTANCE); + types.put(long.class, ZenTypeLong.INSTANCE); + types.put(float.class, ZenTypeFloat.INSTANCE); + types.put(double.class, ZenTypeDouble.INSTANCE); + types.put(void.class, ZenTypeVoid.INSTANCE); + + types.put(Boolean.class, ZenTypeBoolObject.INSTANCE); + types.put(Byte.class, ZenTypeByteObject.INSTANCE); + types.put(Short.class, ZenTypeShortObject.INSTANCE); + types.put(Integer.class, ZenTypeIntObject.INSTANCE); + types.put(Long.class, ZenTypeLongObject.INSTANCE); + types.put(Float.class, ZenTypeFloatObject.INSTANCE); + types.put(Double.class, ZenTypeDoubleObject.INSTANCE); + + types.put(String.class, ZenTypeString.INSTANCE); + types.put(List.class, new ZenTypeArrayBasic(ZenTypeAny.INSTANCE)); + } + + public ZenType getClassType(Class cls) { + if (types.containsKey(cls)) { + return types.get(cls); + } else if (cls.isArray()) { + ZenType result = new ZenTypeArrayBasic(getType(cls.getComponentType())); + types.put(cls, result); + return result; + } else { + ZenTypeNative result = new ZenTypeNative(cls); + types.put(cls, result); + result.complete(this); + return result; + } + } + + @Override + public ZenType getType(Type type) { + if (type instanceof ParameterizedType) { + ParameterizedType pType = (ParameterizedType) type; + Type raw = pType.getRawType(); + if (raw instanceof Class) { + Class rawClass = (Class) raw; + if (List.class.isAssignableFrom(rawClass)) { + return getListType(pType); + } else if (Map.class.isAssignableFrom(rawClass)) { + return getMapType(pType); + } else { + return getClassType(rawClass); + } + } else { + return getType(raw); + } + } else if (type instanceof Class) { + return getClassType((Class) type); + } else { + // TODO: cannot retrieve type + return null; + } + } + + private ZenType getListType(ParameterizedType type) { + if (type.getRawType() == List.class) { + return new ZenTypeArrayList(getType(type.getActualTypeArguments()[0])); + } + + return null; + } + + private ZenType getMapType(ParameterizedType type) { + if (type.getRawType() == Map.class) { + return new ZenTypeAssociative( + getType(type.getActualTypeArguments()[1]), + getType(type.getActualTypeArguments()[0])); + } + + return null; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/definitions/Import.java b/ZenScript/src/main/java/stanhebben/zenscript/definitions/Import.java new file mode 100644 index 0000000..a661c07 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/definitions/Import.java @@ -0,0 +1,38 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.definitions; + +import java.util.List; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class Import { + private final ZenPosition position; + private final List name; + private final String rename; + + public Import(ZenPosition position, List name, String rename) { + this.position = position; + this.name = name; + this.rename = rename; + } + + public ZenPosition getPosition() { + return position; + } + + public List getName() { + return name; + } + + public String getRename() { + return rename; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/definitions/ParsedFunction.java b/ZenScript/src/main/java/stanhebben/zenscript/definitions/ParsedFunction.java new file mode 100644 index 0000000..be03e12 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/definitions/ParsedFunction.java @@ -0,0 +1,134 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.definitions; + +import static stanhebben.zenscript.ZenParser.*; + +import java.util.ArrayList; +import java.util.List; +import stanhebben.zenscript.ZenParser; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.parser.Token; +import stanhebben.zenscript.statements.Statement; +import stanhebben.zenscript.type.ZenTypeAny; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedFunction { + public static ParsedFunction parse(ZenParser parser, IEnvironmentGlobal environment) { + parser.next(); + Token tName = parser.required(ZenParser.T_ID, "identifier expected"); + + // function (argname [as type], argname [as type], ...) [as type] { ...contents... } + parser.required(T_BROPEN, "( expected"); + + List arguments = new ArrayList(); + if (parser.optional(T_BRCLOSE) == null) { + Token argName = parser.required(T_ID, "identifier expected"); + ZenType type = ZenTypeAny.INSTANCE; + if (parser.optional(T_AS) != null) { + type = ZenType.read(parser, environment); + } + + arguments.add(new ParsedFunctionArgument(argName.getValue(), type)); + + while (parser.optional(T_COMMA) != null) { + Token argName2 = parser.required(T_ID, "identifier expected"); + ZenType type2 = ZenTypeAny.INSTANCE; + if (parser.optional(T_AS) != null) { + type2 = ZenType.read(parser, environment); + } + + arguments.add(new ParsedFunctionArgument(argName2.getValue(), type2)); + } + + parser.required(T_BRCLOSE, ") expected"); + } + + ZenType type = ZenTypeAny.INSTANCE; + if (parser.optional(T_AS) != null) { + type = ZenType.read(parser, environment); + } + + parser.required(T_AOPEN, "{ expected"); + + Statement[] statements; + if (parser.optional(T_ACLOSE) != null) { + statements = new Statement[0]; + } else { + ArrayList statementsAL = new ArrayList(); + + while (parser.optional(T_ACLOSE) == null) { + statementsAL.add(Statement.read(parser, environment)); + } + statements = statementsAL.toArray(new Statement[statementsAL.size()]); + } + + return new ParsedFunction(tName.getPosition(), tName.getValue(), arguments, type, statements); + } + + private final ZenPosition position; + private final String name; + private final List arguments; + private final ZenType returnType; + private final Statement[] statements; + + private final String signature; + + private ParsedFunction(ZenPosition position, String name, List arguments, ZenType returnType, Statement[] statements) { + this.position = position; + this.name = name; + this.arguments = arguments; + this.returnType = returnType; + this.statements = statements; + + StringBuilder sig = new StringBuilder(); + sig.append("("); + for (ParsedFunctionArgument argument : arguments) { + sig.append(argument.getType().getSignature()); + } + sig.append(")"); + sig.append(returnType.getSignature()); + signature = sig.toString(); + } + + public ZenPosition getPosition() { + return position; + } + + public String getName() { + return name; + } + + public String getSignature() { + return signature; + } + + public ZenType getReturnType() { + return returnType; + } + + public List getArguments() { + return arguments; + } + + public ZenType[] getArgumentTypes() { + ZenType[] result = new ZenType[arguments.size()]; + for (int i = 0; i < arguments.size(); i++) { + result[i] = arguments.get(i).getType(); + } + return result; + } + + public Statement[] getStatements() { + return statements; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/definitions/ParsedFunctionArgument.java b/ZenScript/src/main/java/stanhebben/zenscript/definitions/ParsedFunctionArgument.java new file mode 100644 index 0000000..bdf89de --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/definitions/ParsedFunctionArgument.java @@ -0,0 +1,25 @@ +package stanhebben.zenscript.definitions; + +import stanhebben.zenscript.type.ZenType; + +/** + * + * @author Stanneke + */ +public class ParsedFunctionArgument { + private final String name; + private final ZenType type; + + public ParsedFunctionArgument(String name, ZenType type) { + this.name = name; + this.type = type; + } + + public String getName() { + return name; + } + + public ZenType getType() { + return type; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/Expression.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/Expression.java new file mode 100644 index 0000000..e8400bd --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/Expression.java @@ -0,0 +1,93 @@ +package stanhebben.zenscript.expression; + +import org.objectweb.asm.Label; +import stanhebben.zenscript.ZenParser; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.parser.expression.ParsedExpression; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeBool; +import stanhebben.zenscript.util.ZenPosition; + +public abstract class Expression implements IPartialExpression { + public static final Expression parse(ZenParser parser, IEnvironmentMethod environment) { + return ParsedExpression.read(parser, environment) + .compile(environment) + .eval(environment); + } + + private final ZenPosition position; + + public Expression(ZenPosition position) { + this.position = position; + } + + public ZenPosition getPosition() { + return position; + } + + public abstract ZenType getType(); + + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, ZenType type) { + if (getType() == type) { + return this; + } else if (getType().canCastImplicit(type, environment)) { + return new ExpressionAs(position, this, type); + } else { + environment.error(position, "Cannot cast " + getType() + " to " + type); + return new ExpressionInvalid(position, type); + } + } + + public abstract void compile(boolean result, IEnvironmentMethod environment); + + public void compileIf(Label onElse, IEnvironmentMethod environment) { + if (getType() == ZenTypeBool.INSTANCE) { + compile(true, environment); + environment.getOutput().ifEQ(onElse); + } else if (getType().isPointer()) { + compile(true, environment); + environment.getOutput().ifNull(onElse); + } else { + throw new RuntimeException("cannot compile non-pointer non-boolean value to if condition"); + } + } + + // ######################################### + // ### IPartialExpression implementation ### + // ######################################### + + @Override + public Expression eval(IEnvironmentGlobal environment) { + return this; + } + + @Override + public Expression assign(ZenPosition position, IEnvironmentGlobal environment, Expression other) { + environment.error(position, "not a valid lvalue"); + return new ExpressionInvalid(position, getType()); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return getType().getMember(position, environment, this, name); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentMethod environment, Expression... values) { + return getType().call(position, environment, this, values); + } + + @Override + public IZenSymbol toSymbol() { + return null; + } + + @Override + public ZenType toType(IEnvironmentGlobal environment) { + environment.error(position, "not a valid type"); + return ZenType.ANY; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionAndAnd.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionAndAnd.java new file mode 100644 index 0000000..474f441 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionAndAnd.java @@ -0,0 +1,53 @@ +package stanhebben.zenscript.expression; + +import org.objectweb.asm.Label; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +public class ExpressionAndAnd extends Expression { + private final Expression a; + private final Expression b; + + public ExpressionAndAnd(ZenPosition position, Expression a, Expression b) { + super(position); + + this.a = a; + this.b = b; + } + + @Override + public ZenType getType() { + return a.getType(); + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + // if not a: return false + // if not b: return false + // return true + + MethodOutput output = environment.getOutput(); + + Label skip = new Label(); + a.compile(true, environment); + output.ifEQ(skip); + b.compile(true, environment); + output.ifEQ(skip); + output.iConst1(); + output.label(skip); + + if (!result) { + output.pop(); + } + } + + @Override + public void compileIf(Label onElse, IEnvironmentMethod environment) { + a.compile(true, environment); + environment.getOutput().ifEQ(onElse); + b.compile(true, environment); + environment.getOutput().ifEQ(onElse); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArgument.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArgument.java new file mode 100644 index 0000000..78e8846 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArgument.java @@ -0,0 +1,37 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionArgument extends Expression { + private final int id; + private final ZenType type; + + public ExpressionArgument(ZenPosition position, int id, ZenType type) { + super(position); + + this.id = id; + this.type = type; + } + + @Override + public ZenType getType() { + return type; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + environment.getOutput().load(type.toASMType(), id); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArithmeticBinary.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArithmeticBinary.java new file mode 100644 index 0000000..eb8d588 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArithmeticBinary.java @@ -0,0 +1,173 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeBool; +import stanhebben.zenscript.type.ZenTypeByte; +import stanhebben.zenscript.type.ZenTypeDouble; +import stanhebben.zenscript.type.ZenTypeFloat; +import stanhebben.zenscript.type.ZenTypeInt; +import stanhebben.zenscript.type.ZenTypeLong; +import stanhebben.zenscript.type.ZenTypeShort; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionArithmeticBinary extends Expression { + private final OperatorType operator; + private final Expression a; + private final Expression b; + + public ExpressionArithmeticBinary(ZenPosition position, OperatorType operator, Expression a, Expression b) { + super(position); + + this.operator = operator; + this.a = a; + this.b = b; + } + + @Override + public ZenType getType() { + return a.getType(); + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + if (result) { + a.compile(result, environment); + b.compile(result, environment); + + ZenType type = a.getType(); + MethodOutput output = environment.getOutput(); + + if (type == ZenTypeBool.INSTANCE) { + switch (operator) { + case AND: + output.iAnd(); + break; + case OR: + output.iOr(); + break; + case XOR: + output.iXor(); + break; + default: + throw new RuntimeException("Unsupported operator on " + type + ": " + operator); + } + } else if (type == ZenTypeByte.INSTANCE || type == ZenTypeShort.INSTANCE || type == ZenTypeInt.INSTANCE) { + switch (operator) { + case ADD: + output.iAdd(); + break; + case SUB: + output.iSub(); + break; + case MUL: + output.iMul(); + break; + case DIV: + output.iDiv(); + break; + case MOD: + output.iRem(); + break; + case AND: + output.iAnd(); + break; + case OR: + output.iOr(); + break; + case XOR: + output.iXor(); + break; + default: + throw new RuntimeException("Unsupported operator on " + type + ": " + operator); + } + } else if (type == ZenTypeLong.INSTANCE) { + switch (operator) { + case ADD: + output.lAdd(); + break; + case SUB: + output.lSub(); + break; + case MUL: + output.lMul(); + break; + case DIV: + output.lDiv(); + break; + case MOD: + output.lRem(); + break; + case AND: + output.lAnd(); + break; + case OR: + output.lOr(); + break; + case XOR: + output.lXor(); + break; + default: + throw new RuntimeException("Unsupported operator on " + type + ": " + operator); + } + } else if (type == ZenTypeFloat.INSTANCE) { + switch (operator) { + case ADD: + output.fAdd(); + break; + case SUB: + output.fSub(); + break; + case MUL: + output.fMul(); + break; + case DIV: + output.fDiv(); + break; + case MOD: + output.fRem(); + break; + default: + throw new RuntimeException("Unsupported operator on " + type + ": " + operator); + } + } else if (type == ZenTypeDouble.INSTANCE) { + switch (operator) { + case ADD: + output.dAdd(); + break; + case SUB: + output.dSub(); + break; + case MUL: + output.dMul(); + break; + case DIV: + output.dDiv(); + break; + case MOD: + output.dRem(); + break; + default: + throw new RuntimeException("Unsupported operator on " + type + ": " + operator); + } + } else { + throw new RuntimeException("Internal compilation error: " + type + " is not a supported arithmetic type"); + } + } else { + a.compile(result, environment); + b.compile(result, environment); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArithmeticCompare.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArithmeticCompare.java new file mode 100644 index 0000000..974004d --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArithmeticCompare.java @@ -0,0 +1,104 @@ +package stanhebben.zenscript.expression; + +import org.objectweb.asm.Label; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeBool; +import stanhebben.zenscript.type.ZenTypeByte; +import stanhebben.zenscript.type.ZenTypeDouble; +import stanhebben.zenscript.type.ZenTypeFloat; +import stanhebben.zenscript.type.ZenTypeInt; +import stanhebben.zenscript.type.ZenTypeLong; +import stanhebben.zenscript.type.ZenTypeShort; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +public class ExpressionArithmeticCompare extends Expression { + private final Expression a; + private final Expression b; + private final CompareType type; + + public ExpressionArithmeticCompare(ZenPosition position, CompareType type, Expression a, Expression b) { + super(position); + + this.a = a; + this.b = b; + this.type = type; + } + + @Override + public ZenType getType() { + return ZenTypeBool.INSTANCE; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + a.compile(result, environment); + b.compile(result, environment); + + if (result) { + MethodOutput output = environment.getOutput(); + if (a.getType() == ZenTypeBool.INSTANCE) { + if (type == CompareType.EQ) { + Label onThen = new Label(); + Label onEnd = new Label(); + + output.ifICmpEQ(onThen); + output.iConst0(); + output.goTo(onEnd); + output.label(onThen); + output.iConst1(); + output.label(onEnd); + } else if (type == CompareType.NE) { + Label onThen = new Label(); + Label onEnd = new Label(); + + output.ifICmpNE(onThen); + output.iConst0(); + output.goTo(onEnd); + output.label(onThen); + output.iConst1(); + output.label(onEnd); + } else { + environment.error(getPosition(), "this kind of comparison is not supported on bool values"); + } + } else { + Label onThen = new Label(); + Label onEnd = new Label(); + + if (a.getType() == ZenTypeLong.INSTANCE) { + output.lCmp(); + } else if (a.getType() == ZenTypeFloat.INSTANCE) { + output.fCmp(); + } else if (a.getType() == ZenTypeDouble.INSTANCE) { + output.dCmp(); + } else if (a.getType() == ZenTypeByte.INSTANCE + || a.getType() == ZenTypeShort.INSTANCE + || a.getType() == ZenTypeInt.INSTANCE) { + // nothing to do + } else { + throw new RuntimeException("Unsupported type for arithmetic compare"); + } + + switch (type) { + case EQ: output.ifICmpEQ(onThen); break; + case NE: output.ifICmpNE(onThen); break; + case LE: output.ifICmpLE(onThen); break; + case GE: output.ifICmpGE(onThen); break; + case LT: output.ifICmpLT(onThen); break; + case GT: output.ifICmpGT(onThen); break; + default: + environment.error(getPosition(), "this kind of comparison is not supported on int values"); + return; + } + + output.iConst0(); + output.goTo(onEnd); + output.label(onThen); + output.iConst1(); + output.label(onEnd); + } + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArithmeticUnary.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArithmeticUnary.java new file mode 100644 index 0000000..95d0ef0 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArithmeticUnary.java @@ -0,0 +1,75 @@ +package stanhebben.zenscript.expression; + +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeBool; +import stanhebben.zenscript.type.ZenTypeByte; +import stanhebben.zenscript.type.ZenTypeDouble; +import stanhebben.zenscript.type.ZenTypeFloat; +import stanhebben.zenscript.type.ZenTypeInt; +import stanhebben.zenscript.type.ZenTypeLong; +import stanhebben.zenscript.type.ZenTypeShort; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +public class ExpressionArithmeticUnary extends Expression { + private final Expression base; + private final OperatorType operator; + + public ExpressionArithmeticUnary(ZenPosition position, OperatorType operator, Expression base) { + super(position); + + this.base = base; + this.operator = operator; + } + + @Override + public ZenType getType() { + return base.getType(); + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + base.compile(result, environment); + + MethodOutput output = environment.getOutput(); + if (result) { + ZenType type = base.getType(); + if (type == ZenTypeBool.INSTANCE) { + if (operator == OperatorType.NOT) { + output.iNot(); + return; + } + } else if (type == ZenTypeByte.INSTANCE || type == ZenTypeShort.INSTANCE || type == ZenTypeInt.INSTANCE) { + if (operator == OperatorType.NOT) { + output.iNot(); + return; + } else if (operator == OperatorType.NEG) { + output.iNeg(); + return; + } + } else if (type == ZenTypeLong.INSTANCE) { + if (operator == OperatorType.NOT) { + output.lNot(); + return; + } else if (operator == OperatorType.NEG) { + output.lNeg(); + return; + } + } else if (type == ZenTypeFloat.INSTANCE) { + if (operator == OperatorType.NEG) { + output.fNeg(); + return; + } + } else if (type == ZenTypeDouble.INSTANCE) { + if (operator == OperatorType.NEG) { + output.fNeg(); + return; + } + } + } + + environment.error(getPosition(), "Invalid operation"); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArray.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArray.java new file mode 100644 index 0000000..1c7e9dc --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArray.java @@ -0,0 +1,68 @@ +package stanhebben.zenscript.expression; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; + +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeAny; +import stanhebben.zenscript.type.ZenTypeArrayBasic; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +public class ExpressionArray extends Expression { + private final Expression[] contents; + private final ZenTypeArrayBasic type; + + public ExpressionArray(ZenPosition position, Expression... contents) { + super(position); + + this.contents = contents; + type = new ZenTypeArrayBasic(ZenTypeAny.INSTANCE); + } + + public ExpressionArray(ZenPosition position, Expression[] contents, ZenTypeArrayBasic type) { + super(position); + this.contents = contents; + this.type = type; + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, ZenType type) { + if (type instanceof ZenTypeArrayBasic) { + ZenTypeArrayBasic arrayType = (ZenTypeArrayBasic) type; + Expression[] newContents = new Expression[contents.length]; + for (int i = 0; i < contents.length; i++) { + newContents[i] = contents[i].cast(position, environment, arrayType.getBaseType()); + } + + return new ExpressionArray(getPosition(), newContents, arrayType); + } else if (this.type.canCastImplicit(type, environment)) { + return this.type.cast(position, environment, this, type); + } else { + environment.error(position, "cannot cast " + this.type + " to " + type); + return new ExpressionInvalid(position, type); + } + } + + @Override + public ZenType getType() { + return type; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + Type baseType = type.getBaseType().toASMType(); + + MethodOutput output = environment.getOutput(); + output.constant(contents.length); + output.newArray(baseType); + + for (int i = 0; i < contents.length; i++) { + output.dup(); + output.constant(i); + contents[i].compile(result, environment); + output.arrayStore(baseType); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArrayGet.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArrayGet.java new file mode 100644 index 0000000..60f9fd7 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArrayGet.java @@ -0,0 +1,45 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeArray; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +public class ExpressionArrayGet extends Expression { + private final Expression array; + private final Expression index; + private final ZenType baseType; + + public ExpressionArrayGet(ZenPosition position, Expression array, Expression index) { + super(position); + + this.array = array; + this.index = index; + this.baseType = ((ZenTypeArray) array.getType()).getBaseType(); + } + + @Override + public ZenType getType() { + return baseType; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + array.compile(result, environment); + index.compile(result, environment); + + if (result) { + environment.getOutput().arrayLoad(baseType.toASMType()); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArrayLength.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArrayLength.java new file mode 100644 index 0000000..e77c8d3 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArrayLength.java @@ -0,0 +1,36 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeInt; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionArrayLength extends Expression { + private final Expression value; + + public ExpressionArrayLength(ZenPosition position, Expression value) { + super(position); + + this.value = value; + } + + @Override + public ZenType getType() { + return ZenTypeInt.INSTANCE; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + environment.getOutput().arrayLength(); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArraySet.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArraySet.java new file mode 100644 index 0000000..c50f816 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionArraySet.java @@ -0,0 +1,45 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +public class ExpressionArraySet extends Expression { + private final Expression array; + private final Expression index; + private final Expression value; + + public ExpressionArraySet(ZenPosition position, Expression array, Expression index, Expression value) { + super(position); + + this.array = array; + this.index = index; + this.value = value; + } + + @Override + public ZenType getType() { + return ZenType.VOID; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + array.compile(result, environment); + index.compile(result, environment); + value.compile(result, environment); + + if (result) { + environment.getOutput().arrayStore(value.getType().toASMType()); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionAs.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionAs.java new file mode 100644 index 0000000..9339b94 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionAs.java @@ -0,0 +1,39 @@ +package stanhebben.zenscript.expression; + +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +public class ExpressionAs extends Expression { + private final Expression value; + private final ZenType type; + + public ExpressionAs(ZenPosition position, Expression value, ZenType type) { + super(position); + + this.value = value; + this.type = type; + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, ZenType type) { + if (!this.type.canCastExplicit(type, environment)) { + environment.error(position, "cannot cast " + this.type + " to " + type); + } + return new ExpressionAs(getPosition(), this, type); + } + + @Override + public ZenType getType() { + return type; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + value.compile(result, environment); + if (result) { + value.getType().compileCast(getPosition(), environment, type); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionBool.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionBool.java new file mode 100644 index 0000000..f2bdd4c --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionBool.java @@ -0,0 +1,71 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import org.objectweb.asm.Label; +import stanhebben.zenscript.compiler.EnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.compiler.EnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeBool; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionBool extends Expression { + private final boolean value; + + public ExpressionBool(ZenPosition position, boolean value) { + super(position); + + this.value = value; + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, ZenType type) { + if (type == ZenTypeBool.INSTANCE) { + return this; + } else if (type.canCastExplicit(type, environment)) { + return new ExpressionAs(position, this, type); + } else { + environment.error(position, "Cannot cast a bool constant to " + type.toString()); + return new ExpressionInvalid(position, type); + } + } + + @Override + public Expression getMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + environment.error(position, "Bool constants do not have members"); + return new ExpressionInvalid(position, ZenTypeBool.INSTANCE); + } + + @Override + public ZenType getType() { + return ZenTypeBool.INSTANCE; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + if (result) { + if (value) { + environment.getOutput().iConst1(); + } else { + environment.getOutput().iConst0(); + } + } + } + + @Override + public void compileIf(Label onElse, IEnvironmentMethod environment) { + if (!value) { + environment.getOutput().goTo(onElse); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionCompareGeneric.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionCompareGeneric.java new file mode 100644 index 0000000..12065fe --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionCompareGeneric.java @@ -0,0 +1,63 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import org.objectweb.asm.Label; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeBool; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionCompareGeneric extends Expression { + private final Expression value; // should return a compareTo value (<=>0) + private final CompareType type; + + public ExpressionCompareGeneric(ZenPosition position, Expression value, CompareType type) { + super(position); + + this.value = value; + this.type = type; + } + + @Override + public ZenType getType() { + return ZenTypeBool.INSTANCE; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) {; + value.compile(result, environment); + if (result) { + MethodOutput output = environment.getOutput(); + Label lblThen = new Label(); + Label lblEnd = new Label(); + + switch (type) { + case LT: output.ifLT(lblThen); break; + case GT: output.ifGT(lblThen); break; + case LE: output.ifLE(lblThen); break; + case GE: output.ifGE(lblThen); break; + case NE: output.ifNE(lblThen); break; + case EQ: output.ifEQ(lblThen); break; + default: + throw new RuntimeException(); + } + + output.iConst0(); + output.goTo(lblEnd); + output.label(lblThen); + output.iConst1(); + output.label(lblEnd); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionConditional.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionConditional.java new file mode 100644 index 0000000..132e400 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionConditional.java @@ -0,0 +1,38 @@ +package stanhebben.zenscript.expression; + +import org.objectweb.asm.Label; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +public class ExpressionConditional extends Expression { + private final Expression condition; + private final Expression onIf; + private final Expression onElse; + + public ExpressionConditional(ZenPosition position, Expression condition, Expression onIf, Expression onElse) { + super(position); + + this.condition = condition; + this.onIf = onIf; + this.onElse = onElse; + } + + @Override + public ZenType getType() { + return onIf.getType(); // TODO: improve + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + Label lblElse = new Label(); + Label lblExit = new Label(); + + condition.compileIf(lblElse, environment); + onIf.compile(result, environment); + environment.getOutput().goTo(lblExit); + environment.getOutput().label(lblElse); + onElse.compile(result, environment); + environment.getOutput().label(lblExit); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionFloat.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionFloat.java new file mode 100644 index 0000000..cacb831 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionFloat.java @@ -0,0 +1,47 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeDouble; +import stanhebben.zenscript.type.ZenTypeFloat; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionFloat extends Expression { + private final double value; + private final ZenType type; + + public ExpressionFloat(ZenPosition position, double value, ZenType type) { + super(position); + + this.value = value; + this.type = type; + } + + @Override + public ZenType getType() { + return type; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + if (!result) return; + + if (type == ZenTypeFloat.INSTANCE) { + environment.getOutput().constant((float) value); + } else if (type == ZenTypeDouble.INSTANCE) { + environment.getOutput().constant(value); + } else { + // TODO: internal error + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionFunction.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionFunction.java new file mode 100644 index 0000000..b755b24 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionFunction.java @@ -0,0 +1,98 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import java.lang.reflect.Method; +import java.util.List; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.definitions.ParsedFunctionArgument; +import stanhebben.zenscript.statements.Statement; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeAny; +import stanhebben.zenscript.type.ZenTypeFunction; +import stanhebben.zenscript.type.ZenTypeNative; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionFunction extends Expression { + private final List arguments; + private final ZenType returnType; + private final Statement[] statements; + + private final ZenTypeFunction functionType; + + public ExpressionFunction(ZenPosition position, List arguments, ZenType returnType, Statement[] statements) { + super(position); + + this.arguments = arguments; + this.returnType = returnType; + this.statements = statements; + + ZenType[] argumentTypes = new ZenType[arguments.size()]; + for (int i = 0; i < arguments.size(); i++) { + argumentTypes[i] = arguments.get(i).getType(); + } + functionType = new ZenTypeFunction(returnType, argumentTypes); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, ZenType type) { + if (type instanceof ZenTypeNative) { + ZenTypeNative nativeType = (ZenTypeNative) type; + Class nativeClass = nativeType.getNativeClass(); + if (nativeClass.isInterface() && nativeClass.getMethods().length == 1) { + // functional interface + Method method = nativeClass.getMethods()[0]; + if (returnType != ZenTypeAny.INSTANCE + && !returnType.canCastImplicit(environment.getType(method.getGenericReturnType()), environment)) { + environment.error(position, "return type not compatible"); + return new ExpressionInvalid(position); + } + if (arguments.size() != method.getParameterTypes().length) { + environment.error(position, "number of arguments incorrect"); + return new ExpressionInvalid(position); + } + for (int i = 0; i < arguments.size(); i++) { + ZenType argumentType = environment.getType(method.getGenericParameterTypes()[i]); + if (arguments.get(i).getType() != ZenTypeAny.INSTANCE + && !argumentType.canCastImplicit(arguments.get(i).getType(), environment)) { + environment.error(position, "argument " + i + " doesn't match"); + return new ExpressionInvalid(position); + } + } + + return new ExpressionJavaLambda( + position, + nativeClass, + arguments, + statements, + environment.getType(nativeClass)); + } else { + environment.error(position, type.toString() + " is not a functional interface"); + return new ExpressionInvalid(position); + } + } else { + environment.error(position, "Cannot cast a function literal to " + type.toString()); + return new ExpressionInvalid(position); + } + } + + @Override + public ZenType getType() { + return functionType; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + // TODO: implement + throw new UnsupportedOperationException("not yet implemented"); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionInt.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionInt.java new file mode 100644 index 0000000..b0e242b --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionInt.java @@ -0,0 +1,74 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeByte; +import stanhebben.zenscript.type.ZenTypeInt; +import stanhebben.zenscript.type.ZenTypeLong; +import stanhebben.zenscript.type.ZenTypeShort; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionInt extends Expression { + private final long value; + private final ZenType type; + + public ExpressionInt(ZenPosition position, long value, ZenType type) { + super(position); + + this.value = value; + this.type = type; + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, ZenType type) { + if (type == this.type) return this; + + switch (type.getNumberType()) { + case ZenType.NUM_BYTE: + case ZenType.NUM_SHORT: + case ZenType.NUM_INT: + case ZenType.NUM_LONG: + return new ExpressionInt(getPosition(), value, type); + case ZenType.NUM_FLOAT: + case ZenType.NUM_DOUBLE: + return new ExpressionFloat(getPosition(), value, type); + } + + return super.cast(position, environment, type); + } + + @Override + public ZenType getType() { + return type; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + if (!result) return; + + MethodOutput output = environment.getOutput(); + if (type == ZenTypeByte.INSTANCE) { + output.biPush((byte) value); + } else if (type == ZenTypeShort.INSTANCE) { + output.siPush((short) value); + } else if (type == ZenTypeInt.INSTANCE) { + output.constant((int) value); + } else if (type == ZenTypeLong.INSTANCE) { + output.constant(value); + } else { + // TODO: internal error + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionIntegerRange.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionIntegerRange.java new file mode 100644 index 0000000..7fc03c9 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionIntegerRange.java @@ -0,0 +1,35 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package stanhebben.zenscript.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionIntegerRange extends Expression { + private final Expression from; + private final Expression to; + + public ExpressionIntegerRange(ZenPosition position, Expression from, Expression to) { + super(position); + + this.from = from; + this.to = to; + } + + @Override + public ZenType getType() { + return null; // TODO: implement + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + // TODO: implement + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionInvalid.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionInvalid.java new file mode 100644 index 0000000..6b49f04 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionInvalid.java @@ -0,0 +1,63 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import org.objectweb.asm.Label; +import stanhebben.zenscript.compiler.EnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.compiler.EnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeAny; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionInvalid extends Expression { + private final ZenType type; + + public ExpressionInvalid(ZenPosition position) { + super(position); + + type = ZenTypeAny.INSTANCE; + } + + public ExpressionInvalid(ZenPosition position, ZenType type) { + super(position); + + this.type = type; + } + + @Override + public Expression getMember(ZenPosition position, IEnvironmentGlobal errors, String name) { + return new ExpressionInvalid(position); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal errors, ZenType type) { + return new ExpressionInvalid(position, type); + } + + @Override + public ZenType getType() { + return type; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + if (result) { + environment.getOutput().aConstNull(); + } + } + + @Override + public void compileIf(Label onElse, IEnvironmentMethod environment) { + environment.getOutput().goTo(onElse); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaCallStatic.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaCallStatic.java new file mode 100644 index 0000000..8a06467 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaCallStatic.java @@ -0,0 +1,52 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionJavaCallStatic extends Expression { + private final JavaMethod method; + private final Expression[] arguments; + + public ExpressionJavaCallStatic( + ZenPosition position, + JavaMethod method, + Expression... arguments) { + super(position); + + this.method = method; + this.arguments = arguments; + } + + @Override + public ZenType getType() { + return method.getReturnType(); + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + MethodOutput output = environment.getOutput(); + + for (Expression argument : arguments) { + argument.compile(true, environment); + } + + output.invokeStatic(method); + + if (method.getReturnType() != ZenType.VOID && !result) { + output.pop(method.getReturnType().isLarge()); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaCallStaticGenerated.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaCallStaticGenerated.java new file mode 100644 index 0000000..6c4f62f --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaCallStaticGenerated.java @@ -0,0 +1,59 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionJavaCallStaticGenerated extends Expression { + private final String target; + private final String name; + private final String descriptor; + + private final ZenType returnZType; + private final Expression[] arguments; + + public ExpressionJavaCallStaticGenerated( + ZenPosition position, + String target, String name, String descriptor, + ZenType returnZType, + Expression... arguments) { + super(position); + + this.target = target; + this.name = name; + this.descriptor = descriptor; + + this.returnZType = returnZType; + this.arguments = arguments; + } + + @Override + public ZenType getType() { + return returnZType; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + MethodOutput output = environment.getOutput(); + + for (Expression argument : arguments) { + argument.compile(true, environment); + } + + output.invokeStatic(target, name, descriptor); + if (!descriptor.endsWith("V") && !result) { + output.pop(); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaCallVirtual.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaCallVirtual.java new file mode 100644 index 0000000..8d42efa --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaCallVirtual.java @@ -0,0 +1,55 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionJavaCallVirtual extends Expression { + private final JavaMethod method; + + private final Expression receiver; + private final Expression[] arguments; + + public ExpressionJavaCallVirtual( + ZenPosition position, + JavaMethod method, + Expression receiver, + Expression... arguments) { + super(position); + + this.method = method; + + this.receiver = receiver; + this.arguments = arguments; + } + + @Override + public ZenType getType() { + return method.getReturnType(); + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + receiver.compile(true, environment); + + for (Expression argument : arguments) { + argument.compile(true, environment); + } + + environment.getOutput().invoke(method); + if (method.getReturnType() != ZenType.VOID && !result) { + environment.getOutput().pop(method.getReturnType().isLarge()); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaLambda.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaLambda.java new file mode 100644 index 0000000..535e075 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaLambda.java @@ -0,0 +1,100 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import java.lang.reflect.Method; +import java.util.List; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; +import stanhebben.zenscript.compiler.EnvironmentClass; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.compiler.EnvironmentMethod; +import stanhebben.zenscript.compiler.IEnvironmentClass; +import stanhebben.zenscript.definitions.ParsedFunctionArgument; +import stanhebben.zenscript.statements.Statement; +import stanhebben.zenscript.symbols.SymbolArgument; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.descriptor; +import static stanhebben.zenscript.util.ZenTypeUtil.internal; + +/** + * + * @author Stanneke + */ +public class ExpressionJavaLambda extends Expression { + private final Class interfaceClass; + private final List arguments; + private final Statement[] statements; + + private final ZenType type; + + public ExpressionJavaLambda( + ZenPosition position, + Class interfaceClass, + List arguments, + Statement[] statements, + ZenType type) { + super(position); + + this.interfaceClass = interfaceClass; + this.arguments = arguments; + this.statements = statements; + + this.type = type; + } + + @Override + public ZenType getType() { + return type; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + if (!result) return; + + Method method = interfaceClass.getMethods()[0]; + + // generate class + String clsName = environment.makeClassName(); + + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); + cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, clsName, null, "java/lang/Object", new String[] { internal(interfaceClass) }); + + MethodOutput constructor = new MethodOutput(cw, Opcodes.ACC_PUBLIC, "", "()V", null, null); + constructor.start(); + constructor.loadObject(0); + constructor.invokeSpecial("java/lang/Object", "", "()V"); + constructor.ret(); + constructor.end(); + + MethodOutput output = new MethodOutput(cw, Opcodes.ACC_PUBLIC, method.getName(), descriptor(method), null, null); + + IEnvironmentClass environmentClass = new EnvironmentClass(cw, environment); + IEnvironmentMethod environmentMethod = new EnvironmentMethod(output, environmentClass); + + for (int i = 0; i < arguments.size(); i++) { + environmentMethod.putValue( + arguments.get(i).getName(), + new SymbolArgument(i + 1, environment.getType(method.getGenericParameterTypes()[i]))); + } + + output.start(); + for (Statement statement : statements) { + statement.compile(environmentMethod); + } + output.end(); + + environment.putClass(clsName, cw.toByteArray()); + + // make class instance + environment.getOutput().newObject(clsName); + environment.getOutput().dup(); + environment.getOutput().construct(clsName); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaMethodCurled.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaMethodCurled.java new file mode 100644 index 0000000..cb05ca4 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaMethodCurled.java @@ -0,0 +1,55 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import java.util.ArrayList; +import java.util.List; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.definitions.ParsedFunctionArgument; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeFunction; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionJavaMethodCurled extends Expression { + private final JavaMethod method; + private final ZenType type; + private final Expression receiver; + + public ExpressionJavaMethodCurled( + ZenPosition position, + JavaMethod method, + IEnvironmentGlobal environment, + Expression receiver) { + super(position); + + this.method = method; + this.receiver = receiver; + + List arguments = new ArrayList(); + for (int i = 0; i < method.getMethod().getParameterTypes().length; i++) { + arguments.add(new ParsedFunctionArgument("p" + i, environment.getType(method.getMethod().getGenericParameterTypes()[i]))); + } + + this.type = new ZenTypeFunction(environment.getType(method.getMethod().getGenericReturnType()), arguments); + } + + @Override + public ZenType getType() { + return type; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + // TODO: compile + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaMethodStatic.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaMethodStatic.java new file mode 100644 index 0000000..5e317d5 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionJavaMethodStatic.java @@ -0,0 +1,49 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import java.util.ArrayList; +import java.util.List; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.definitions.ParsedFunctionArgument; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeFunction; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionJavaMethodStatic extends Expression { + private final JavaMethod method; + private final ZenType type; + + public ExpressionJavaMethodStatic(ZenPosition position, JavaMethod method, IEnvironmentGlobal environment) { + super(position); + + this.method = method; + + List arguments = new ArrayList(); + for (int i = 0; i < method.getParameterTypes().length; i++) { + arguments.add(new ParsedFunctionArgument("p" + i, environment.getType(method.getMethod().getGenericParameterTypes()[i]))); + } + + this.type = new ZenTypeFunction(environment.getType(method.getMethod().getGenericReturnType()), arguments); + } + + @Override + public ZenType getType() { + return type; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + // TODO: compile + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionLocalGet.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionLocalGet.java new file mode 100644 index 0000000..6f9bdd9 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionLocalGet.java @@ -0,0 +1,37 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.symbols.SymbolLocal; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionLocalGet extends Expression { + private final SymbolLocal variable; + + public ExpressionLocalGet(ZenPosition position, SymbolLocal variable) { + super(position); + + this.variable = variable; + } + + @Override + public ZenType getType() { + return variable.getType(); + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + int local = environment.getLocal(variable); + environment.getOutput().load(variable.getType().toASMType(), local); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionLocalSet.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionLocalSet.java new file mode 100644 index 0000000..3be4259 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionLocalSet.java @@ -0,0 +1,44 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.symbols.SymbolLocal; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionLocalSet extends Expression { + private final SymbolLocal variable; + private final Expression value; + + public ExpressionLocalSet(ZenPosition position, SymbolLocal variable, Expression value) { + super(position); + + this.variable = variable; + this.value = value; + } + + @Override + public ZenType getType() { + return variable.getType(); + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + int local = environment.getLocal(variable); + + value.compile(true, environment); + if (result) { + environment.getOutput().dup(); + } + environment.getOutput().store(variable.getType().toASMType(), local); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionMapContains.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionMapContains.java new file mode 100644 index 0000000..c9a3073 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionMapContains.java @@ -0,0 +1,45 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import java.util.Map; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeBool; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.internal; + +/** + * + * @author Stanneke + */ +public class ExpressionMapContains extends Expression { + private final Expression map; + private final Expression key; + + public ExpressionMapContains(ZenPosition position, Expression map, Expression key) { + super(position); + + this.map = map; + this.key = key; + } + + @Override + public ZenType getType() { + return ZenTypeBool.INSTANCE; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + if (result) { + map.compile(result, environment); + key.compile(result, environment); + + environment.getOutput().invokeInterface(internal(Map.class), "containsKey", "()Ljava/lang/Object;"); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionMapIndexGet.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionMapIndexGet.java new file mode 100644 index 0000000..c45330e --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionMapIndexGet.java @@ -0,0 +1,51 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import java.util.Map; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeAssociative; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.internal; + +/** + * + * @author Stanneke + */ +public class ExpressionMapIndexGet extends Expression { + private final Expression map; + private final Expression index; + + private final ZenType type; + + public ExpressionMapIndexGet(ZenPosition position, Expression map, Expression index) { + super(position); + + this.map = map; + this.index = index; + + type = ((ZenTypeAssociative) map.getType()).getValueType(); + } + + @Override + public ZenType getType() { + return type; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + if (result) { + map.compile(result, environment); + index.compile(result, environment); + environment.getOutput().invokeInterface( + internal(Map.class), + "get", + "(Ljava/lang/Object;)Ljava/lang/Object;"); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionMapIndexSet.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionMapIndexSet.java new file mode 100644 index 0000000..3818245 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionMapIndexSet.java @@ -0,0 +1,53 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import java.util.Map; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeVoid; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.internal; + +/** + * + * @author Stanneke + */ +public class ExpressionMapIndexSet extends Expression { + private final Expression map; + private final Expression index; + private final Expression value; + + private final ZenType type; + + public ExpressionMapIndexSet(ZenPosition position, Expression map, Expression index, Expression value) { + super(position); + + this.map = map; + this.index = index; + this.value = value; + + type = ZenTypeVoid.INSTANCE; + } + + @Override + public ZenType getType() { + return type; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + if (result) { + map.compile(result, environment); + index.compile(result, environment); + environment.getOutput().invokeInterface( + internal(Map.class), + "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionMapSize.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionMapSize.java new file mode 100644 index 0000000..72d1b4d --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionMapSize.java @@ -0,0 +1,41 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import java.util.Map; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeInt; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.internal; + +/** + * + * @author Stanneke + */ +public class ExpressionMapSize extends Expression { + private final Expression map; + + public ExpressionMapSize(ZenPosition position, Expression map) { + super(position); + + this.map = map; + } + + @Override + public ZenType getType() { + return ZenTypeInt.INSTANCE; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + if (result) { + map.compile(true, environment); + environment.getOutput().invokeInterface(internal(Map.class), "size", "()I"); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionNull.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionNull.java new file mode 100644 index 0000000..f6d72b1 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionNull.java @@ -0,0 +1,51 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import org.objectweb.asm.Label; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeNull; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionNull extends Expression { + public ExpressionNull(ZenPosition position) { + super(position); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, ZenType type) { + if (type.isPointer()) { + return this; + } else { + environment.error(position, "Cannot convert null to " + type); + return new ExpressionInvalid(position); + } + } + + @Override + public ZenType getType() { + return ZenTypeNull.INSTANCE; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + if (result) { + environment.getOutput().aConstNull(); + } + } + + @Override + public void compileIf(Label onElse, IEnvironmentMethod environment) { + environment.getOutput().goTo(onElse); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionOrOr.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionOrOr.java new file mode 100644 index 0000000..a741012 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionOrOr.java @@ -0,0 +1,27 @@ +package stanhebben.zenscript.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +public class ExpressionOrOr extends Expression { + private final Expression a; + private final Expression b; + + public ExpressionOrOr(ZenPosition position, Expression a, Expression b) { + super(position); + + this.a = a; + this.b = b; + } + + @Override + public ZenType getType() { + return a.getType(); // TODO: combine both types + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + // TODO: implement + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionString.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionString.java new file mode 100644 index 0000000..a5ef50e --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionString.java @@ -0,0 +1,38 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeString; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionString extends Expression { + private final String value; + + public ExpressionString(ZenPosition position, String value) { + super(position); + + this.value = value; + } + + @Override + public ZenType getType() { + return ZenTypeString.INSTANCE; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + if (result) { + environment.getOutput().constant(value); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionStringConcat.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionStringConcat.java new file mode 100644 index 0000000..5bc92e9 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionStringConcat.java @@ -0,0 +1,62 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression; + +import java.util.List; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeString; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ExpressionStringConcat extends Expression { + private final List values; + + public ExpressionStringConcat(ZenPosition position, List values) { + super(position); + + this.values = values; + } + + public void add(Expression value) { + values.add(value); + } + + @Override + public ZenType getType() { + return ZenTypeString.INSTANCE; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + if (result) { + MethodOutput output = environment.getOutput(); + + // Step 1: construct StringBuilder + output.newObject(StringBuilder.class); + output.dup(); + output.construct(StringBuilder.class); + + // Step 2: concatenate Strings + for (Expression value : values) { + value.compile(true, environment); + output.invoke(StringBuilder.class, "append", StringBuilder.class, String.class); + } + + // Step 3: return String + output.invoke(StringBuilder.class, "toString", String.class); + } else { + for (Expression value : values) { + value.compile(false, environment); + } + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionTable.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionTable.java new file mode 100644 index 0000000..bd5d648 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/ExpressionTable.java @@ -0,0 +1,84 @@ +package stanhebben.zenscript.expression; + +import java.util.HashMap; +import java.util.Map; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeAny; +import stanhebben.zenscript.type.ZenTypeAssociative; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.internal; + +public class ExpressionTable extends Expression { + private final Expression[] keys; + private final Expression[] values; + private final ZenType type; + + public ExpressionTable(ZenPosition position, Expression[] keys, Expression[] values) { + super(position); + + this.keys = keys; + this.values = values; + + type = new ZenTypeAssociative(ZenTypeAny.INSTANCE, ZenTypeAny.INSTANCE); + } + + public ExpressionTable(ZenPosition position, Expression[] keys, Expression[] values, ZenType type) { + super(position); + + this.keys = keys; + this.values = values; + + this.type = type; + } + + @Override + public ZenType getType() { + return type; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + if (result) { + MethodOutput output = environment.getOutput(); + output.newObject(HashMap.class); + output.dup(); + output.invokeSpecial(internal(HashMap.class), "", "()V"); + + for (int i = 0; i < keys.length; i++) { + output.dup(); + keys[i].compile(true, environment); + values[i].compile(true, environment); + output.invokeInterface(internal(Map.class), "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + output.pop(); + } + } else { + for (Expression key : keys) { + key.compile(false, environment); + } + for (Expression value : values) { + value.compile(false, environment); + } + } + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, ZenType type) { + if (type instanceof ZenTypeAssociative) { + ZenTypeAssociative keyType = (ZenTypeAssociative) type; + Expression[] newKeys = new Expression[keys.length]; + Expression[] newValues = new Expression[values.length]; + + for (int i = 0; i < keys.length; i++) { + newKeys[i] = keys[i].cast(position, environment, keyType.getKeyType()); + newValues[i] = values[i].cast(position, environment, keyType.getValueType()); + } + + return new ExpressionTable(getPosition(), newKeys, newValues, type); + } else { + return super.cast(position, environment, type); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/ExpressionJavaStaticField.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/ExpressionJavaStaticField.java new file mode 100644 index 0000000..73d7a04 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/ExpressionJavaStaticField.java @@ -0,0 +1,44 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression.partial; + +import java.lang.reflect.Field; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.compiler.ITypeRegistry; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +public class ExpressionJavaStaticField extends Expression { + private final Class cls; + private final Field field; + private final ITypeRegistry types; + + public ExpressionJavaStaticField(ZenPosition position, Class cls, Field field, ITypeRegistry types) { + super(position); + + this.cls = cls; + this.field = field; + this.types = types; + } + + @Override + public ZenType getType() { + return types.getType(field.getGenericType()); + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + if (result) { + environment.getOutput().getStaticField(cls, field); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/IPartialExpression.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/IPartialExpression.java new file mode 100644 index 0000000..5df0157 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/IPartialExpression.java @@ -0,0 +1,32 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression.partial; + +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public interface IPartialExpression { + public Expression eval(IEnvironmentGlobal environment); + + public Expression assign(ZenPosition position, IEnvironmentGlobal environment, Expression other); + + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, String name); + + public Expression call(ZenPosition position, IEnvironmentMethod environment, Expression... values); + + public IZenSymbol toSymbol(); + + public ZenType toType(IEnvironmentGlobal environment); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialJavaClass.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialJavaClass.java new file mode 100644 index 0000000..53c397b --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialJavaClass.java @@ -0,0 +1,64 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression.partial; + +import java.lang.reflect.Type; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.symbols.SymbolJavaClass; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class PartialJavaClass implements IPartialExpression { + private final ZenPosition position; + private final Type cls; + + public PartialJavaClass(ZenPosition position, Type cls) { + this.position = position; + this.cls = cls; + } + + @Override + public Expression eval(IEnvironmentGlobal environment) { + environment.error(position, "cannot use class name as expression"); + return new ExpressionInvalid(position); + } + + @Override + public Expression assign(ZenPosition position, IEnvironmentGlobal environment, Expression other) { + environment.error(position, "cannot assign to a class name"); + return new ExpressionInvalid(position); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return environment.getType(cls).getStaticMember(position, environment, name); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentMethod environment, Expression... values) { + environment.error(position, "cannot call a class name"); + return new ExpressionInvalid(position); + } + + @Override + public IZenSymbol toSymbol() { + return new SymbolJavaClass(cls); + } + + @Override + public ZenType toType(IEnvironmentGlobal environment) { + return environment.getType(cls); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialLocal.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialLocal.java new file mode 100644 index 0000000..45b55ae --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialLocal.java @@ -0,0 +1,68 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression.partial; + +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.ExpressionLocalGet; +import stanhebben.zenscript.expression.ExpressionLocalSet; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.symbols.SymbolLocal; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class PartialLocal implements IPartialExpression { + private final ZenPosition position; + private final SymbolLocal variable; + + public PartialLocal(ZenPosition position, SymbolLocal variable) { + this.position = position; + this.variable = variable; + } + + @Override + public Expression eval(IEnvironmentGlobal environment) { + return new ExpressionLocalGet(position, variable); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return variable.getType().getMember(position, environment, this, name); + } + + @Override + public Expression assign(ZenPosition position, IEnvironmentGlobal environment, Expression other) { + if (variable.isFinal()) { + environment.error(position, "value cannot be changed"); + return new ExpressionInvalid(position); + } else { + return new ExpressionLocalSet(position, variable, other); + } + } + + @Override + public Expression call(ZenPosition position, IEnvironmentMethod environment, Expression... values) { + return variable.getType().call(position, environment, eval(environment), values); + } + + @Override + public IZenSymbol toSymbol() { + return variable; + } + + @Override + public ZenType toType(IEnvironmentGlobal environment) { + environment.error(position, "not a valid type"); + return ZenType.ANY; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialPackage.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialPackage.java new file mode 100644 index 0000000..d812170 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialPackage.java @@ -0,0 +1,70 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression.partial; + +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.symbols.SymbolPackage; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class PartialPackage implements IPartialExpression { + private final ZenPosition position; + private final SymbolPackage contents; + + public PartialPackage(ZenPosition position, SymbolPackage contents) { + this.position = position; + this.contents = contents; + } + + @Override + public Expression eval(IEnvironmentGlobal environment) { + environment.error(position, "Cannot use package name as expression"); + return new ExpressionInvalid(position); + } + + @Override + public Expression assign(ZenPosition position, IEnvironmentGlobal environment, Expression other) { + environment.error(position, "Cannot assign to a package"); + return new ExpressionInvalid(position); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + IZenSymbol member = contents.get(name); + if (member == null) { + environment.error(position, "No such member: " + name); + return new ExpressionInvalid(position); + } else { + return member.instance(position); + } + } + + @Override + public Expression call(ZenPosition position, IEnvironmentMethod environment, Expression... values) { + environment.error(position, "cannot call a package"); + return new ExpressionInvalid(position); + } + + @Override + public IZenSymbol toSymbol() { + return null; // not supposed to be used as symbol + } + + @Override + public ZenType toType(IEnvironmentGlobal environment) { + environment.error(position, "not a valid type"); + return ZenType.ANY; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialStaticGenerated.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialStaticGenerated.java new file mode 100644 index 0000000..14f6f05 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialStaticGenerated.java @@ -0,0 +1,83 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression.partial; + +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.ExpressionJavaCallStaticGenerated; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.symbols.SymbolZenStaticMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +public class PartialStaticGenerated implements IPartialExpression { + private final ZenPosition position; + private final String owner; + private final String method; + private final String signature; + private final ZenType[] argumentTypes; + private final ZenType returnType; + + public PartialStaticGenerated(ZenPosition position, String owner, String method, String signature, ZenType[] argumentTypes, ZenType returnType) { + this.position = position; + this.owner = owner; + this.method = method; + this.signature = signature; + this.argumentTypes = argumentTypes; + this.returnType = returnType; + } + + @Override + public Expression eval(IEnvironmentGlobal environment) { + environment.error(position, "cannot use a function as value"); + return new ExpressionInvalid(position); + } + + @Override + public Expression assign(ZenPosition position, IEnvironmentGlobal environment, Expression other) { + environment.error(position, "cannot assign to a function"); + return new ExpressionInvalid(position); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + environment.error(position, "functions don't have members"); + return new ExpressionInvalid(position); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentMethod environment, Expression... values) { + if (values.length != argumentTypes.length) { + environment.error(position, "invalid number of arguments"); + return new ExpressionInvalid(position); + } + + Expression[] arguments = new Expression[argumentTypes.length]; + for (int i = 0; i < arguments.length; i++) { + arguments[i] = values[i].cast(position, environment, argumentTypes[i]); + } + + return new ExpressionJavaCallStaticGenerated(position, owner, method, signature, returnType, arguments); + } + + @Override + public IZenSymbol toSymbol() { + return new SymbolZenStaticMethod(owner, method, signature, argumentTypes, returnType); + } + + @Override + public ZenType toType(IEnvironmentGlobal environment) { + environment.error(position, "not a valid type"); + return ZenType.ANY; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialStaticGetter.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialStaticGetter.java new file mode 100644 index 0000000..ac8e6e2 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialStaticGetter.java @@ -0,0 +1,64 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression.partial; + +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.symbols.SymbolJavaStaticGetter; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class PartialStaticGetter implements IPartialExpression { + private final ZenPosition position; + private final JavaMethod method; + + public PartialStaticGetter(ZenPosition position, JavaMethod method) { + this.position = position; + this.method = method; + } + + @Override + public Expression eval(IEnvironmentGlobal environment) { + return method.callStatic(position, environment); + } + + @Override + public Expression assign(ZenPosition position, IEnvironmentGlobal environment, Expression other) { + environment.error(position, "cannot alter this final"); + return new ExpressionInvalid(position); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return method.getReturnType().getMember(position, environment, this, name); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentMethod environment, Expression... values) { + environment.error(position, "value cannot be called"); + return new ExpressionInvalid(position); + } + + @Override + public IZenSymbol toSymbol() { + return new SymbolJavaStaticGetter(method); + } + + @Override + public ZenType toType(IEnvironmentGlobal environment) { + environment.error(position, "not a valid type"); + return ZenType.ANY; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialStaticMethod.java b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialStaticMethod.java new file mode 100644 index 0000000..4220a99 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/expression/partial/PartialStaticMethod.java @@ -0,0 +1,69 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.expression.partial; + +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.symbols.SymbolJavaStaticMethod; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class PartialStaticMethod implements IPartialExpression { + private final ZenPosition position; + private final JavaMethod method; + + public PartialStaticMethod(ZenPosition position, JavaMethod method) { + this.position = position; + this.method = method; + } + + @Override + public Expression eval(IEnvironmentGlobal environment) { + environment.error(position, "not a valid expression"); + return new ExpressionInvalid(position); + } + + @Override + public Expression assign(ZenPosition position, IEnvironmentGlobal environment, Expression other) { + environment.error(position, "cannot alter this final"); + return new ExpressionInvalid(position); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + environment.error(position, "methods have no members"); + return new ExpressionInvalid(position); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentMethod environment, Expression... values) { + if (method.accepts(environment, values)) { + return method.callStatic(position, environment, values); + } else { + environment.error(position, "parameter count mismatch"); + return new ExpressionInvalid(position, method.getReturnType()); + } + } + + @Override + public IZenSymbol toSymbol() { + return new SymbolJavaStaticMethod(method); + } + + @Override + public ZenType toType(IEnvironmentGlobal environment) { + return ZenType.ANY; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/ArrayListI.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/ArrayListI.java new file mode 100644 index 0000000..b200ee4 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/ArrayListI.java @@ -0,0 +1,72 @@ +/* + * This file is subject to the license.txt file in the main folder + * of this project. + */ + +package stanhebben.zenscript.parser; + +/** + * This class is similar to an ArrayList, but is optimized for integers. + * + * @author Stan Hebben + */ +public class ArrayListI { + private int[] data; + private int size; + + /** + * Creates a new, empty ArrayList of integers. + */ + public ArrayListI() { + data = new int[8]; + } + + /** + * Adds a value. + * + * @param value integer value + */ + public void add(int value) { + if (size == data.length) expand(); + data[size++] = value; + } + + /** + * Gets the value at the specified position. + * + * @param index position + * @return value at this position + */ + public int get(int index) { + if (index >= size) throw new ArrayIndexOutOfBoundsException(); + return data[index]; + } + + /** + * Gets the size of this ArrayList. + * + * @return size + */ + public int size() { + return size; + } + + //////////////////// + // Private methods + //////////////////// + + /** + * Doubles the size of this array. + */ + private void expand() { + int[] newdata = new int[data.length * 2]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + + public int[] toArray() { + int[] result = new int[size]; + System.arraycopy(data, 0, result, 0, size); + return result; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/CharStream.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/CharStream.java new file mode 100644 index 0000000..1e230dd --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/CharStream.java @@ -0,0 +1,119 @@ +/* + * This file is subject to the license.txt file in the main folder + * of this project. + */ + +package stanhebben.zenscript.parser; + +/** + * Represents a character stream. Suitable for character by character parsing. + * + * Used in the regular expression parser. + * + * @author Stan Hebben + */ +public class CharStream { + private char[] data; + private int index; + + /** + * Constructs a character stream from a data string. + * + * @param data data string + */ + public CharStream(String data) { + this.data = data.toCharArray(); + index = 0; + } + + /** + * Checks if the next character in the stream equals the specified character. + * + * @param ch checked character + * @return true if the next character equals the specified character + */ + public boolean peek(char ch) { + if (index == data.length) return false; + return data[index] == ch; + } + + /** + * Processes an optional character. If the next character does not equal + * the specified character, nothing will happen. + * + * @param ch checked character + * @return true if the next character equals the specified character + */ + public boolean optional(char ch) { + if (index == data.length) return false; + if (data[index] == ch) { + index++; + return true; + } else { + return false; + } + } + + /** + * Processes an optional range of characters. If the next character is not + * within the specified bounds, nothing will happen. + * + * @param from lower character value bound, inclusive + * @param to upper character value bound, inclusive + * @return the processed character + */ + public char optional(char from, char to) { + if (index == data.length) return 0; + if (data[index] >= from && data[index] <= to) { + return data[index++]; + } else { + return 0; + } + } + + /** + * Processes a required character. If the next character does not equal the + * specified character, an IllegalArgumentException is thrown. + * + * @param ch required character + */ + public void required(char ch) { + if (data[index] != ch) { + throw new IllegalArgumentException("Unexpected character: " + data[index]); + } + index++; + } + + /** + * Processes a required character range. If the next character is not within + * the specified bounds, an IllegalArgumentException is thrown. + * + * @param from lower character value bound, inclusive + * @param to upper character value bound, inclusive + * @return the processed character + */ + public char required(char from, char to) { + if (data[index] < from || data[index] > to) { + throw new IllegalArgumentException("Unexpected character: " + data[index]); + } + return data[index++]; + } + + /** + * Returns the next character in the stream. + * + * @return the next character + */ + public char next() { + return data[index++]; + } + + /** + * Checks if the end of the stream has been reached. + * + * @return true if there are more characters in the stream + */ + public boolean hasMore() { + return index < data.length; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/CompiledDFA.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/CompiledDFA.java new file mode 100644 index 0000000..5e614b3 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/CompiledDFA.java @@ -0,0 +1,152 @@ +/* + * This file is subject to the license.txt file in the main folder + * of this project. + */ + +package stanhebben.zenscript.parser; + +import static java.util.Arrays.sort; + +/** + * Represents a compiled DFA. A compiled DFA has a compact representation and + * is immediately usable for efficient processing. + * + * A compiled DFA can be converted to a compact integer array. This array can + * then be hardcoded in an application. + * + * @author Stan Hebben + */ +public class CompiledDFA { + public static final int NOFINAL = Integer.MIN_VALUE; + + public HashMapII[] transitions; + public int[] finals; + + /** + * Constructs a compiled DFA from the specified transition graph and finals + * arrays. + * + * The transition array specifies all transitions for each state. The finals + * array specifies the final class index of each state, or NOFINAL if the state + * is not a final. There can multiple final classes, which can, for example, + * be used to distinguish token types. + * + * @param transitions transitions graph + * @param finals finals + */ + public CompiledDFA(HashMapII[] transitions, int[] finals) { + this.transitions = transitions; + this.finals = finals; + } + + /** + * Converts an integer array with a DFA definition (generated by toArray()) + * to a CompiledDFA. + * + * @param definition definition array + */ + public CompiledDFA(int[] definition) { + int ix = 0; + int numStates = definition[ix++]; + transitions = new HashMapII[numStates]; + finals = new int[numStates]; + + for (int i = 0; i < numStates; i++) { + transitions[i] = new HashMapII(); + finals[i] = definition[ix++]; + + int numRanges = definition[ix++]; + for (int j = 0; j < numRanges; j++) { + int from = definition[ix++]; + int to = definition[ix++]; + int state = definition[ix++]; + for (int k = from; k <= to; k++) { + transitions[i].put(k, state); + } + } + + int numSingles = definition[ix++]; + for (int j = 0; j < numSingles; j++) { + int label = definition[ix++]; + int state = definition[ix++]; + transitions[i].put(label, state); + } + } + } + + /** + * Converts the DFA to an integer array. + * + * @return an integer array representation of the DFA + */ + public int[] toArray() { + ArrayListI result = new ArrayListI(); + result.add(finals.length); + + for (int i = 0; i < finals.length; i++) { + result.add(finals[i]); + + ArrayListI singles = new ArrayListI(); + ArrayListI rangeFrom = new ArrayListI(); + ArrayListI rangeTo = new ArrayListI(); + + int[] keys = transitions[i].keysArray(); + sort(keys); + for (int j = 0; j < keys.length; j++) { + int from = j; + int state = transitions[i].get(j); + while (j + 1 < keys.length && keys[j + 1] == keys[j] + 1 && transitions[i].get(j + 1) == state) j++; + if (j == from) { + singles.add(keys[j]); + } else { + rangeFrom.add(keys[from]); + rangeTo.add(keys[j]); + } + } + + result.add(rangeFrom.size()); + for (int j = 0; j < rangeFrom.size(); j++) { + result.add(rangeFrom.get(j)); + result.add(rangeTo.get(j)); + result.add(transitions[i].get(rangeFrom.get(j))); + } + result.add(singles.size()); + for (int j = 0; j < singles.size(); j++) { + result.add(singles.get(j)); + result.add(transitions[i].get(singles.get(j))); + } + } + return result.toArray(); + } + + /* Used for debugging */ + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + for (int i = 0; i < transitions.length; i++) { + HashMapII map = transitions[i]; + + IteratorI it = map.keys(); + while (it.hasNext()) { + int v = it.next(); + result.append("edge("); + result.append(i); + result.append(", "); + result.append(v); + result.append("): "); + result.append(map.get(v)); + result.append("\r\n"); + } + } + for (int i = 0; i < finals.length; i++) { + if (finals[i] != DFA.NOFINAL) { + result.append("final("); + result.append(i); + result.append("): "); + result.append(finals[i]); + result.append("\r\n"); + } + } + return result.toString(); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/DFA.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/DFA.java new file mode 100644 index 0000000..604699a --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/DFA.java @@ -0,0 +1,249 @@ +/* + * This file is subject to the license.txt file in the main folder + * of this project. + */ + +package stanhebben.zenscript.parser; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Queue; + +/** + * Implements a DFA. + * + * @author Stan Hebben + */ +public class DFA { + public static final int NOFINAL = Integer.MIN_VALUE; + + private DFAState initial; + + /** + * Constructs a new DFA with the specified initial state. + * + * @param initial + */ + public DFA(DFAState initial) { + this.initial = initial; + } + + /** + * Compiles this DFA into a more efficient structure. + * + * @return the compiled DFA + */ + public CompiledDFA compile() { + ArrayList nodeList = new ArrayList(); + HashMap nodes = new HashMap(); + nodes.put(initial, 0); + nodeList.add(initial); + + /* Find all reachable nodes in the dfs */ + int counter = 1; + Queue todo = new LinkedList(); + todo.add(initial); + + while (!todo.isEmpty()) { + DFAState current = todo.poll(); + + IteratorI it = current.transitions.keys(); + while (it.hasNext()) { + int k = it.next(); + DFAState next = current.transitions.get(k); + if (!nodes.containsKey(next)) { + todo.add(next); + nodes.put(next, counter++); + nodeList.add(next); + } + } + } + + /* Compile */ + HashMapII[] transitions = new HashMapII[counter]; + int[] finals2 = new int[counter]; + + for (DFAState node : nodeList) { + int index = nodes.get(node); + finals2[index] = node.finalCode; + + transitions[index] = new HashMapII(); + IteratorI it = node.transitions.keys(); + while (it.hasNext()) { + int k = it.next(); + DFAState next = node.transitions.get(k); + transitions[index].put(k, nodes.get(next)); + } + } + + return new CompiledDFA(transitions, finals2); + } + + /** + * Generates the minimal version of this DFA. + * + * @return the minimal DFA + */ + public DFA optimize() { + CompiledDFA compiled = compile(); + HashMapII[] transitions = compiled.transitions; + int size = transitions.length; + + /* Collect all edges and determine alphabet */ + HashSetI alphabet = new HashSetI(); + for (int i = 0; i < size; i++) { + IteratorI it = transitions[i].keys(); + while (it.hasNext()) { + int k = it.next(); + alphabet.add(k); + } + } + + /* Initialize distinguishing array */ + boolean[][] distinguishable = new boolean[size + 1][size + 1]; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + distinguishable[i][j] = compiled.finals[i] != compiled.finals[j]; + } + } + for (int i = 0; i < size; i++) { + distinguishable[i][size] = true; + distinguishable[size][i] = true; + } + + /* Minimization algorithm implementation */ + boolean changed; + do { + changed = false; + IteratorI ita = alphabet.iterator(); + while (ita.hasNext()) { + int x = ita.next(); + for (int i = 0; i < size; i++) { + int ti = transitions[i].get(x, size); + for (int j = 0; j < size; j++) { + if (distinguishable[i][j]) continue; + + int tj = transitions[j].get(x, size); + if (distinguishable[ti][tj]) { + distinguishable[i][j] = true; + changed = true; + } + } + } + } + } while (changed); + + /* Group nodes */ + HashMapI nodeMap = new HashMapI(); + outer: for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + if (!distinguishable[i][j] && nodeMap.containsKey(j)) { + nodeMap.put(i, nodeMap.get(j)); + if (compiled.finals[i] != NOFINAL) { + if (nodeMap.get(j).getFinal() != NOFINAL && nodeMap.get(j).getFinal() != compiled.finals[i]) { + throw new RuntimeException("Eh?"); + } + } + continue outer; + } + } + DFAState node = new DFAState(); + node.setFinal(compiled.finals[i]); + nodeMap.put(i, node); + } + + for (int i = 0; i < compiled.transitions.length; i++) { + IteratorI iter = transitions[i].keys(); + while (iter.hasNext()) { + int k = iter.next(); + + nodeMap.get(i).addTransition(k, nodeMap.get(transitions[i].get(k))); + } + } + + DFA result = new DFA(nodeMap.get(0)); + return result; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + CompiledDFA dfs = compile(); + for (int i = 0; i < dfs.transitions.length; i++) { + HashMapII map = dfs.transitions[i]; + + IteratorI it = map.keys(); + while (it.hasNext()) { + int v = it.next(); + result.append("edge("); + result.append(i); + result.append(", "); + result.append(v); + result.append("): "); + result.append(map.get(v)); + result.append("\r\n"); + } + } + for (int i = 0; i < dfs.finals.length; i++) { + if (dfs.finals[i] != NOFINAL) { + result.append("final("); + result.append(i); + result.append("): "); + result.append(dfs.finals[i]); + result.append("\r\n"); + } + } + return result.toString(); + } + + ///////////////////////// + // Public inner classes + ///////////////////////// + + /** + * Represents a state in a DFA. + */ + public static class DFAState { + private HashMapI transitions; + private int finalCode = NOFINAL; + + /** + * Creates a new DFA state. + */ + public DFAState() { + transitions = new HashMapI(); + } + + /** + * Adds a transition. + * + * @param label transition edge label + * @param next next state + */ + public void addTransition(int label, DFAState next) { + transitions.put(label, next); + } + + /** + * Sets the final class of this state. Finals can be divided in multiple + * class, in which case each class gets its own index. Class NOFINAL is + * used to indicate nonfinals. + * + * @param finalCode final index + */ + public void setFinal(int finalCode) { + this.finalCode = finalCode; + } + + /** + * Gets the final class of this state. Equals NOFINAL if this state is + * not a final. + * + * @return final index + */ + public int getFinal() { + return finalCode; + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/HashMapI.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/HashMapI.java new file mode 100644 index 0000000..f4e56e0 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/HashMapI.java @@ -0,0 +1,216 @@ +/* + * This file is subject to the license.txt file in the main folder + * of this project. + */ +package stanhebben.zenscript.parser; + +/** + * A HashMap implementation which is optimized for integer keys. + * + * @author Stan Hebben + * @param + */ +public class HashMapI { + private int[] keys; + private T[] values; + private int[] next; + private int mask; + private int size; + + /** + * Creates a new HashMap for integer keys. + */ + public HashMapI() { + keys = new int[16]; + values = (T[])new Object[16]; + next = new int[16]; + mask = 15; + size = 0; + + for (int i = 0; i < keys.length; i++) { + keys[i] = Integer.MIN_VALUE; + } + } + + /** + * Associates the specified value with the specified key in this map. If the + * map previously contained a mapping for the key, the old value is replaced. + * + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + */ + public void put(int key, T value) { + if (size > (keys.length * 3) >> 2) { + expand(); + } + + int index = key & mask; + + if (keys[index] == Integer.MIN_VALUE) { + keys[index] = key; + values[index] = value; + } else { + if (keys[index] == key) { + values[index] = value; + return; + } + while (next[index] != 0) { + index = next[index] - 1; + if (keys[index] == key) { + values[index] = value; + return; + } + } + int ref = index; + while (keys[index] != Integer.MIN_VALUE) { + index++; + if (index == keys.length) index = 0; + } + next[ref] = index + 1; + + keys[index] = key; + values[index] = value; + } + + size++; + } + + /** + * Returns the value to which the specified key is mapped, or null if this + * map contains no mapping for the key. + * + * @param key the key whose associated value is to be returned + * @return the value to which the specified key is mapped, or null if this map contains no mapping for the key + */ + public T get(int key) { + int index = key & mask; + while (keys[index] != key) { + if (next[index] == 0) { + return null; + } + index = next[index] - 1; + } + return values[index]; + } + + /** + * Returns true if this map contains a mapping for the specified key. + * + * @param key The key whose presence in this map is to be tested + * @return true if this map contains a mapping for the specified key. + */ + public boolean containsKey(int key) { + int index = key & mask; + while (keys[index] != key) { + if (next[index] == 0) { + return false; + } + index = next[index] - 1; + } + return true; + } + + /** + * Returns an iterator over the keys in this HashMap. The elements are + * returned in no particular order. + * + * @return an iterator over the keys in this HashMap + */ + public IteratorI keys() { + return new KeyIterator(); + } + + /** + * Returns an array with all keys in this HashMap. The elements are returned + * in no particular order. + * + * @return an array with all keys in this HashMap + */ + public int[] keysArray() { + int[] result = new int[size]; + int ix = 0; + for (int i = 0; i < keys.length; i++) { + if (keys[i] != Integer.MIN_VALUE) result[ix++] = keys[i]; + } + return result; + } + + //////////////////// + // Private methods + //////////////////// + + /** + * Expands the size of this array. (it doubles the size) + */ + private void expand() { + int[] newKeys = new int[keys.length * 2]; + T[] newValues = (T[])new Object[values.length * 2]; + int[] newNext = new int[next.length * 2]; + int newMask = newKeys.length - 1; + + for (int i = 0; i < newKeys.length; i++) { + newKeys[i] = Integer.MIN_VALUE; + } + + for (int i = 0; i < keys.length; i++) { + if (keys[i] == Integer.MIN_VALUE) { + continue; + } + + int key = keys[i]; + T value = values[i]; + int index = key & newMask; + + if (newKeys[index] == Integer.MIN_VALUE) { + newKeys[index] = key; + newValues[index] = value; + } else { + while (newNext[index] != 0) { + index = newNext[index] - 1; + } + int ref = index; + while (newKeys[index] != Integer.MIN_VALUE) { + index = (index + 1) & newMask; + } + newNext[ref] = index + 1; + + newKeys[index] = key; + newValues[index] = value; + } + } + + keys = newKeys; + values = newValues; + next = newNext; + mask = newMask; + } + + ////////////////////////// + // Private inner classes + ////////////////////////// + + private class KeyIterator implements IteratorI { + private int i; + + public KeyIterator() { + i = 0; + skip(); + } + + @Override + public boolean hasNext() { + return i < keys.length; + } + + @Override + public int next() { + int result = keys[i++]; + skip(); + return result; + } + + private void skip() { + while (i < keys.length && keys[i] == Integer.MIN_VALUE) i++; + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/HashMapII.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/HashMapII.java new file mode 100644 index 0000000..fd63096 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/HashMapII.java @@ -0,0 +1,237 @@ +/* + * This file is subject to the license.txt file in the main folder + * of this project. + */ +package stanhebben.zenscript.parser; + +/** + * A HashMap implementation which is optimized to associate integers with integers. + * + * @author Stan Hebben + */ +public class HashMapII { + private int[] keys; + private int[] values; + private int[] next; + private int mask; + private int size; + + /** + * Creates a new HashMap for integer keys and values. + */ + public HashMapII() { + keys = new int[16]; + values = new int[16]; + next = new int[16]; + mask = 15; + size = 0; + + for (int i = 0; i < keys.length; i++) { + keys[i] = Integer.MIN_VALUE; + } + } + + /** + * Returns the number of key/value pairs in the hashmap. + * + * @return hashmap size + */ + public int size() { + return size; + } + + /** + * Associates the specified value with the specified key in this map. If the + * map previously contained a mapping for the key, the old value is replaced. + * + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + */ + public void put(int key, int value) { + if (size > (keys.length * 3) >> 2) { + expand(); + } + + int index = key & mask; + + if (keys[index] == Integer.MIN_VALUE) { + keys[index] = key; + values[index] = value; + } else { + while (next[index] != 0) { + index = next[index] - 1; + if (keys[index] == key) { + values[index] = value; + return; + } + } + int ref = index; + while (keys[index] != Integer.MIN_VALUE) { + index++; + if (index == keys.length) index = 0; + } + next[ref] = index + 1; + + keys[index] = key; + values[index] = value; + } + + size++; + } + + /** + * Returns the value to which the specified key is mapped, or null if this + * map contains no mapping for the key. + * + * @param key the key whose associated value is to be returned + * @return the value to which the specified key is mapped, or Integer.MIN_VALUE if this map contains no mapping for the key + */ + public int get(int key) { + int index = key & mask; + while (keys[index] != key) { + if (next[index] == 0) { + return Integer.MIN_VALUE; + } + index = next[index] - 1; + } + return values[index]; + } + + /** + * Returns the value to which the specified key is mapped, or null if this + * map contains no mapping for the key. + * + * @param key the key whose associated value is to be returned + * @param def the default key value + * @return the value to which the specified key is mapped, or the default value if this map contains no mapping for the key + */ + public int get(int key, int def) { + int index = key & mask; + while (keys[index] != key) { + if (next[index] == 0) { + return def; + } + index = next[index] - 1; + } + return values[index]; + } + + /** + * Returns true if this map contains a mapping for the specified key. + * + * @param key The key whose presence in this map is to be tested + * @return true if this map contains a mapping for the specified key. + */ + public boolean containsKey(int key) { + int index = key & mask; + while (keys[index] != key) { + if (next[index] == 0) { + return false; + } + index = next[index] - 1; + } + return true; + } + + /** + * Returns an iterator over the keys in this HashMap. The elements are + * returned in no particular order. + * + * @return an iterator over the keys in this HashMap + */ + public IteratorI keys() { + return new KeyIterator(); + } + + /** + * Returns an array with all keys in this HashMap. The elements are returned + * in no particular order. + * + * @return an array with all keys in this HashMap + */ + public int[] keysArray() { + int[] result = new int[size]; + int ix = 0; + for (int i = 0; i < keys.length; i++) { + if (keys[i] != Integer.MIN_VALUE) result[ix++] = keys[i]; + } + return result; + } + + //////////////////// + // Private methods + //////////////////// + + /** + * Expands the size of this array. (it doubles the size) + */ + private void expand() { + int[] newKeys = new int[keys.length * 2]; + int[] newValues = new int[values.length * 2]; + int[] newNext = new int[next.length * 2]; + int newMask = newKeys.length - 1; + + for (int i = 0; i < newKeys.length; i++) { + newKeys[i] = Integer.MIN_VALUE; + } + + for (int i = 0; i < keys.length; i++) { + if (keys[i] == Integer.MIN_VALUE) { + continue; + } + + int key = keys[i]; + int value = values[i]; + int index = key & newMask; + + if (newKeys[index] == Integer.MIN_VALUE) { + newKeys[index] = key; + newValues[index] = value; + } else { + while (newNext[index] != 0) { + index = newNext[index] - 1; + } + int ref = index; + while (newKeys[index] != Integer.MIN_VALUE) { + index = (index + 1) & newMask; + } + newNext[ref] = index + 1; + + newKeys[index] = key; + newValues[index] = value; + } + } + + keys = newKeys; + values = newValues; + next = newNext; + mask = newMask; + } + + ////////////////////////// + // Private inner classes + ////////////////////////// + + private class KeyIterator implements IteratorI { + private int i; + + public KeyIterator() { + i = 0; + skip(); + } + + public boolean hasNext() { + return i < keys.length; + } + + public int next() { + int result = keys[i++]; + skip(); + return result; + } + + private void skip() { + while (i < keys.length && keys[i] == Integer.MIN_VALUE) i++; + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/HashSetI.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/HashSetI.java new file mode 100644 index 0000000..dc1af8e --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/HashSetI.java @@ -0,0 +1,196 @@ +/* + * This file is subject to the license.txt file in the main folder + * of this project. + */ +package stanhebben.zenscript.parser; + +import java.util.Arrays; + +/** + * HashSet implementation which is optimized for integer values. + * + * @author Stan Hebben + */ +public class HashSetI { + private int[] values; + private int[] next; + private int mask; + private int size; + + /** + * Creates a new HashSet for integer values. + */ + public HashSetI() { + values = new int[16]; + next = new int[16]; + mask = 15; + size = 0; + + for (int i = 0; i < values.length; i++) { + values[i] = Integer.MIN_VALUE; + } + } + + public HashSetI(HashSetI original) { + values = Arrays.copyOf(original.values, original.values.length); + next = Arrays.copyOf(original.next, original.next.length); + mask = original.mask; + size = original.size; + } + + /** + * Adds the specified value to this HashSet. If this value is already in this + * HashSet, nothing happens. + * + * @param value value to be added to the HashSet + */ + public void add(int value) { + if (size > (values.length * 3) >> 2) { + expand(); + } + + int index = value & mask; + + if (values[index] == Integer.MIN_VALUE) { + values[index] = value; + } else { + if (values[index] == value) { + return; + } + while (next[index] != 0) { + index = next[index] - 1; + if (values[index] == value) { + return; + } + } + int ref = index; + while (values[index] != Integer.MIN_VALUE) { + index++; + if (index == values.length) index = 0; + } + next[ref] = index + 1; + + values[index] = value; + } + + size++; + } + + /** + * Checks if this HashSet contains the specified value. + * + * @param value integer value to add + * @return true if this HashSet contains the specified value + */ + public boolean contains(int value) { + int index = value & mask; + while (values[index] != value) { + if (next[index] == 0) { + return false; + } + index = next[index] - 1; + } + return true; + } + + /** + * Returns an iterator over this HashSet. The elements are returned in no + * particular order. + * + * @return an iterator over this HashSet + */ + public IteratorI iterator() { + return new KeyIterator(); + } + + /** + * Converts the contents of this HashSet to an integer array. The order of + * the elements is unspecified. + * + * @return this HashSet as integer array + */ + public int[] toArray() { + int[] result = new int[size]; + int ix = 0; + for (int i = 0; i < values.length; i++) { + if (values[i] != Integer.MIN_VALUE) { + result[ix++] = values[i]; + } + } + return result; + } + + ////////////////////////// + // Private inner methods + ////////////////////////// + + /** + * Expands the capacity of this HashSet. (double it) + */ + private void expand() { + int[] newKeys = new int[values.length * 2]; + int[] newNext = new int[next.length * 2]; + int newMask = newKeys.length - 1; + + for (int i = 0; i < newKeys.length; i++) { + newKeys[i] = Integer.MIN_VALUE; + } + + for (int i = 0; i < values.length; i++) { + if (values[i] == Integer.MIN_VALUE) { + continue; + } + + int key = values[i]; + int index = key & newMask; + + if (newKeys[index] == Integer.MIN_VALUE) { + newKeys[index] = key; + } else { + while (newNext[index] != 0) { + index = newNext[index] - 1; + } + int ref = index; + while (newKeys[index] != Integer.MIN_VALUE) { + index = (index + 1) & newMask; + } + newNext[ref] = index + 1; + + newKeys[index] = key; + } + } + + values = newKeys; + next = newNext; + mask = newMask; + } + + ////////////////////////// + // Private inner classes + ////////////////////////// + + private class KeyIterator implements IteratorI { + private int i; + + public KeyIterator() { + i = 0; + skip(); + } + + @Override + public boolean hasNext() { + return i < values.length; + } + + @Override + public int next() { + int result = values[i++]; + skip(); + return result; + } + + private void skip() { + while (i < values.length && values[i] == Integer.MIN_VALUE) i++; + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/IteratorI.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/IteratorI.java new file mode 100644 index 0000000..fde349b --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/IteratorI.java @@ -0,0 +1,27 @@ + /* + * This file is subject to the license.txt file in the main folder + * of this project. + */ + +package stanhebben.zenscript.parser; + +/** + * This interface represents an iterator over integer values. + * + * @author Stan Hebben + */ +public interface IteratorI { + /** + * Returns true if the iteration has more elements. + * + * @return true if the iteration has more elements + */ + public boolean hasNext(); + + /** + * Returns the next element in this iteration. + * + * @return the next element in this iteration + */ + public int next(); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/NFA.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/NFA.java new file mode 100644 index 0000000..33930a0 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/NFA.java @@ -0,0 +1,574 @@ +/* + * This file is subject to the license.txt file in the main folder + * of this project. + */ + +package stanhebben.zenscript.parser; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Queue; + +/** + * + * @author Stan Hebben + */ +public class NFA { + public static final int NOFINAL = Integer.MIN_VALUE; + public static final int EPSILON = Integer.MIN_VALUE + 1; + + private final NFAState initial; + + private HashMap converted; + + /** + * Creates a new NFA with the specified initial state. + * + * @param initial initial state + */ + public NFA(NFAState initial) { + this.initial = initial; + } + + /** + * Creates a new NFA from the specified regular expression. + * + * Note: the regular expression implementation is not complete. Shorthand + * character classes (\s, \w, \d), hexadecimal and unicode escape sequences and + * unicode properties are not implemented. + * + * Anchors, lazy plus operators, lookbehind and lookforward are not implemented + * since they cannot be implemented in an NFA. + * + * @param regexp regular expression + */ + public NFA(String regexp) { + Partial main = processRegExp(new CharStream(regexp)); + initial = new NFAState(); + initial.addTransition(main.tailLabel, main.tail); + main.head.setFinal(1); + } + + /** + * Converts an array of regular expressions to an NFA. Each regular expression + * can have its own final class. The length of both arrays must match. + * + * @param regexp regular expression array + * @param finals final classes + */ + public NFA(String[] regexp, int[] finals) { + initial = new NFAState(); + + Partial[] partials = new Partial[regexp.length]; + for (int i = 0; i < regexp.length; i++) { + //System.out.println("Processing regexp " + i + ": " + regexp[i]); + partials[i] = processRegExp(new CharStream(regexp[i])); + partials[i].head.setFinal(finals[i]); + initial.addTransition(partials[i].tailLabel, partials[i].tail); + } + + //System.out.println("NFA generated"); + } + + /** + * Converts this NFA to a DFA. The resulting DFA is not optimal. If the + * NFA is ambiguous, the token with lowest value will receive preference. + * + * @return this NFA as DFA + */ + public DFA toDFA() { + converted = new HashMap(); + HashSet closure = new HashSet(); + this.initial.closure(closure); + DFA.DFAState init = convert(new NodeSet(closure)); + + DFA dfs = new DFA(init); + return dfs; + } + + //////////////////// + // Private methods + //////////////////// + + /* Converts a set of possible states to a DFA state. */ + private DFA.DFAState convert(NodeSet nodes) { + if (!converted.containsKey(nodes)) { + DFA.DFAState node = new DFA.DFAState(); + converted.put(nodes, node); + + HashSet edgeSet = new HashSet(); + for (NFAState n : nodes.nodes) { + n.alphabet(edgeSet); + } + for (int i : edgeSet) { + HashSet edge = new HashSet(); + for (NFAState n : nodes.nodes) { + n.edge(i, edge); + } + NodeSet set = new NodeSet(edge); + node.addTransition(i, convert(set)); + } + int finalCode = DFA.NOFINAL; + for (NFAState n : nodes.nodes) { + if (n.finalCode != NOFINAL) { + if (finalCode != DFA.NOFINAL) { + finalCode = Math.min(n.finalCode, finalCode); + } else { + finalCode = n.finalCode; + } + } + } + node.setFinal(finalCode); + + return node; + } + return converted.get(nodes); + } + + /* Processes a regular expression */ + private Partial processRegExp(CharStream stream) { + Partial partial = processRegExp0(stream); + if (stream.optional('|')) { + ArrayList partials = new ArrayList(); + partials.add(partial); + partials.add(processRegExp0(stream)); + while (stream.optional('|')) { + partials.add(processRegExp0(stream)); + } + NFAState head = new NFAState(); + NFAState tail = new NFAState(); + for (Partial p : partials) { + tail.addTransition(p.tailLabel, p.tail); + p.head.addTransition(EPSILON, head); + } + return new Partial(EPSILON, tail, head); + } else { + return partial; + } + } + + /* Processes an element of an alternation clause */ + private Partial processRegExp0(CharStream stream) { + Partial partial = processRegExp1(stream); + while (!stream.peek(')') && !stream.peek('|') && stream.hasMore()) { + Partial partial2 = processRegExp1(stream); + partial.head.addTransition(partial2.tailLabel, partial2.tail); + partial = new Partial(partial.tailLabel, partial.tail, partial2.head); + } + return partial; + } + + /* Processes a partial with optional repetition */ + private Partial processRegExp1(CharStream stream) { + Partial partial = processPartial(stream); + if (stream.optional('*')) { + NFAState node = new NFAState(); + partial.head.addTransition(EPSILON, node); + node.addTransition(partial.tailLabel, partial.tail); + return new Partial(EPSILON, node, node); + } else if (stream.optional('+')) { + NFAState node = new NFAState(); + partial.head.addTransition(EPSILON, node); + node.addTransition(partial.tailLabel, partial.tail); + return new Partial(partial.tailLabel, partial.tail, node); + } else if (stream.optional('?')) { + NFAState node = new NFAState(); + node.addTransition(EPSILON, partial.head); + node.addTransition(partial.tailLabel, partial.tail); + return new Partial(EPSILON, node, partial.head); + } else if (stream.optional('{')) { + int amount = processInt(stream); + if (amount < 0) throw new IllegalArgumentException("Repitition count expected"); + if (stream.optional(',')) { + int amount2 = processInt(stream); + stream.required('}'); + if (amount2 < 0) { + // unbounded + Partial[] duplicates = new Partial[amount]; + for (int i = 0; i < duplicates.length - 1; i++) { + duplicates[i] = partial.duplicate(); + } + duplicates[amount - 1] = partial; + for (int i = 0; i < duplicates.length - 1; i++) { + duplicates[i].head.addTransition(duplicates[i + 1].tailLabel, duplicates[i + 1].tail); + } + duplicates[amount - 1].head.addTransition(duplicates[amount - 1].tailLabel, duplicates[amount - 1].tail); + return new Partial(duplicates[0].tailLabel, duplicates[0].tail, duplicates[amount - 1].head); + } else { + Partial[] duplicates = new Partial[amount2]; + for (int i = 0; i < duplicates.length - 1; i++) { + duplicates[i] = partial.duplicate(); + } + duplicates[amount2 - 1] = partial; + for (int i = 0; i < duplicates.length - 1; i++) { + duplicates[i].head.addTransition(duplicates[i + 1].tailLabel, duplicates[i + 1].tail); + } + for (int i = amount; i < amount2; i++) { + if (i == 0) { + /* insert additional node before the chain because minimal repeat is 0 */ + NFAState additional = new NFAState(); + additional.addTransition(duplicates[0].tailLabel, duplicates[0].tail); + additional.addTransition(EPSILON, duplicates[amount2 - 1].head); + duplicates[0].tailLabel = EPSILON; + duplicates[0].tail = additional; + } else { + duplicates[i - 1].head.addTransition(EPSILON, duplicates[amount2 - 1].head); + } + } + return new Partial(duplicates[0].tailLabel, duplicates[0].tail, duplicates[amount2 - 1].head); + } + } else { + stream.required('}'); + + Partial[] duplicates = new Partial[amount]; + for (int i = 0; i < duplicates.length - 1; i++) { + duplicates[i] = partial.duplicate(); + } + duplicates[amount - 1] = partial; + for (int i = 0; i < duplicates.length - 1; i++) { + duplicates[i].head.addTransition(duplicates[i + 1].tailLabel, duplicates[i + 1].tail); + } + return new Partial(duplicates[0].tailLabel, duplicates[0].tail, duplicates[amount - 1].head); + } + } else { + return partial; + } + } + + /* Processes a part of a regular expression, which can be a character, + * expression between brackets, a dot or a character list */ + private Partial processPartial(CharStream stream) { + if (stream.optional('(')) { + Partial result = processRegExp(stream); + stream.required(')'); + return result; + } else if (stream.optional('[')) { + NFAState head = new NFAState(); + NFAState tail = new NFAState(); + + IteratorI iter = processCharList(stream).iterator(); + while (iter.hasNext()) { + tail.addTransition(iter.next(), head); + } + stream.required(']'); + return new Partial(EPSILON, tail, head); + } else if (stream.optional('.')) { + NFAState head = new NFAState(); + NFAState tail = new NFAState(); + + for (int i = 0; i <= 256; i++) { + tail.addTransition(i, head); + } + return new Partial(EPSILON, tail, head); + } else { + NFAState node = new NFAState(); + return new Partial(processChar(stream), node, node); + } + } + + /* Processes a character list */ + private HashSetI processCharList(CharStream stream) { + boolean invert = stream.optional('^'); + HashSetI base = new HashSetI(); + do { + processCharPartial(base, stream); + } while (!stream.peek(']')); + if (invert) { + HashSetI result = new HashSetI(); + for (int i = 0; i <= 256; i++) { + if (!base.contains(i)) result.add(i); + } + return result; + } else { + return base; + } + } + + /* Processes a character partial, which can be a single character or a range + * of characters. */ + private void processCharPartial(HashSetI out, CharStream stream) { + if (stream.optional('.')) { + for (int i = 0; i <= 256; i++) { + out.add(i); + } + } else { + char from = processChar(stream); + if (stream.optional('-')) { + char to = processChar(stream); + for (int i = from; i <= to; i++) { + out.add(i); + } + } else { + out.add(from); + } + } + } + + /* Processes a single character */ + private char processChar(CharStream stream) { + if (stream.optional('\\')) { + if (stream.optional('r')) return '\r'; + if (stream.optional('n')) return '\n'; + if (stream.optional('t')) return '\t'; + if (stream.optional('[')) return '['; + if (stream.optional(']')) return ']'; + if (stream.optional('(')) return '('; + if (stream.optional(')')) return ')'; + if (stream.optional('.')) return '.'; + if (stream.optional('+')) return '+'; + if (stream.optional('-')) return '-'; + if (stream.optional('\\')) return '\\'; + if (stream.optional('{')) return '{'; + if (stream.optional('}')) return '}'; + if (stream.optional('?')) return '?'; + if (stream.optional('*')) return '*'; + if (stream.optional('~')) return '~'; + if (stream.optional('|')) return '|'; + if (stream.optional('^')) return '^'; + throw new IllegalArgumentException("Invalid character: " + stream.next()); + } else { + if (stream.peek('[') + || stream.peek(']') + || stream.peek('(') + || stream.peek(')') + || stream.peek('{') + || stream.peek('}') + || stream.peek('.') + || stream.peek('-') + || stream.peek('+') + || stream.peek('?') + || stream.peek('*')) { + throw new IllegalArgumentException("Invalid character: " + stream.next()); + } else { + return stream.next(); + } + } + } + + /* Processes an optional integer, returns -1 if the next character do not + * represent an integer */ + private int processInt(CharStream stream) { + int data = stream.optional('0', '9') - '0'; + if (data < 0) return -1; + char ch = stream.optional('0', '9'); + while (ch != 0) { + data = data * 10 + (ch - '0'); + ch = stream.optional('0', '9'); + } + return data; + } + + ///////////////////////// + // Public inner classes + ///////////////////////// + + /** + * Represents an NFA state. + */ + public static class NFAState { + private static int counter = 1; + + private ArrayList transitions; + private ArrayList closure; + private int index; + private int finalCode = NOFINAL; + + /** + * Creates a new state. + */ + public NFAState() { + transitions = new ArrayList(); + index = counter++; + } + + /** + * Adds a transition. + * + * @param label transition label + * @param next next state + */ + public void addTransition(int label, NFAState next) { + transitions.add(new Transition(label, next)); + } + + /** + * Sets the final state of this state. + * + * @param finalCode + */ + public void setFinal(int finalCode) { + if (this.finalCode == finalCode) return; + this.finalCode = finalCode; + + for (Transition t : transitions) { + if (t.label == EPSILON) { + t.next.setFinal(finalCode); + } + } + } + + //////////////////// + // Private methods + //////////////////// + + /** + * Determines the (partial) closure of this state. + * + * @param output output to store the closure in + */ + private void closure(HashSet output) { + for (NFAState node : closure()) output.add(node); + } + + /** + * Calculates the (full) closure of this state. + * + * @return this state's closure + */ + private ArrayList closure() { + if (closure == null) { + HashSet tmp = new HashSet(); + tmp.add(this); + for (Transition transition : transitions) { + if (transition.label == EPSILON) { + if (!tmp.contains(transition.next)) { + tmp.add(transition.next); + transition.next.closure(tmp); + } + } + } + closure = new ArrayList(); + for (NFAState node : tmp) closure.add(node); + } + return closure; + } + + /** + * Calculates the possible set of states after a transition with the + * specified value + * + * @param label transition label + * @param output possible set of states (out) + */ + private void edge(int label, HashSet output) { + for (Transition transition : transitions) { + if (transition.label == label) { + if (!output.contains(transition.next)) { + output.add(transition.next); + transition.next.closure(output); + } + } + } + } + + /** + * Calculates the alphabet of this state. + * + * @param output alphabet (out) + */ + private void alphabet(HashSet output) { + for (NFAState node : closure()) { + for (Transition t : node.transitions) { + if (t.label != EPSILON) { + output.add(t.label); + } + } + } + } + + //////////////// + // Inner class + //////////////// + + /* Represents a transition */ + private class Transition { + private int label; + private NFAState next; + + public Transition(int input, NFAState next) { + this.label = input; + this.next = next; + } + } + } + + /* Hashable set of nodes */ + private class NodeSet { + private NFAState[] nodes; + + public NodeSet(HashSet nodes) { + this.nodes = new NFAState[nodes.size()]; + int i = 0; + for (NFAState node : nodes) this.nodes[i++] = node; + Arrays.sort(this.nodes, new Comparator() { + public int compare(NFAState o1, NFAState o2) { + return o1.index - o2.index; + } + }); + } + + @Override + public int hashCode() { + return Arrays.hashCode(nodes); + } + + @Override + public boolean equals(Object other) { + if (other.getClass() != NodeSet.class) return false; + return Arrays.equals(nodes, ((NodeSet)other).nodes); + } + } + + /** + * Contains a partially parsed regular expression. Has a head and a tail. + * + * The tail is a transition and thus has a label and a next node. The head + * is the 'final' state of the partial. + */ + private static class Partial { + private int tailLabel; + private NFAState tail; + private NFAState head; + + /** + * Creates a new partial + * @param tailLabel tail label + * @param tail tail node + * @param head head node + */ + public Partial(int tailLabel, NFAState tail, NFAState head) { + this.tailLabel = tailLabel; + this.tail = tail; + this.head = head; + } + + /** + * Duplicates the NFA of this partial. + * + * @return a duplicate of this partial + */ + public Partial duplicate() { + HashMap nodes = new HashMap(); + + Queue todo = new LinkedList(); + todo.add(tail); + nodes.put(tail, new NFAState()); + while (!todo.isEmpty()) { + NFAState node = todo.poll(); + for (NFAState.Transition t : node.transitions) { + if (!nodes.containsKey(t.next)) { + nodes.put(t.next, new NFAState()); + todo.add(t.next); + } + nodes.get(node).addTransition(t.label, nodes.get(t.next)); + } + } + + return new Partial(tailLabel, nodes.get(tail), nodes.get(head)); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/ParseException.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/ParseException.java new file mode 100644 index 0000000..c0831c5 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/ParseException.java @@ -0,0 +1,40 @@ +/* + * This file is subject to the license.txt file in the main folder + * of this project. + */ + +package stanhebben.zenscript.parser; + +import stanhebben.zenscript.ZenParsedFile; + +/** + * + * @author Stan + */ +public class ParseException extends RuntimeException { + private final Token token; + private final String message; + + public ParseException(Token token, String error) { + super(token == null ? "Error at end of file - " + error : "Error parsing line " + token.getPosition().getLine() + ":" + token.getPosition().getLineOffset() + " - " + error + " (last token: " + token.getValue() + ")"); + + this.token = token; + this.message = error; + } + + public ZenParsedFile getFile() { + return token == null ? null : token.getPosition().getFile(); + } + + public int getLine() { + return token == null ? -1 : token.getPosition().getLine(); + } + + public int getLineOffset() { + return token == null ? -1 : token.getPosition().getLineOffset(); + } + + public String getExplanation() { + return message; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/Token.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/Token.java new file mode 100644 index 0000000..519d846 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/Token.java @@ -0,0 +1,59 @@ +/* + * This file is subject to the license.txt file in the main folder + * of this project. + */ + +package stanhebben.zenscript.parser; + +import stanhebben.zenscript.util.ZenPosition; + +/** + * Represents a token in a token stream. + * + * @author Stan Hebben + */ +public class Token { + private final ZenPosition position; + private final String value; + private final int type; + + /** + * Constructs a new token. + * + * @param value token string value + * @param type token type + * @param position token position + */ + public Token(String value, int type, ZenPosition position) { + this.value = value; + this.type = type; + this.position = position; + } + + public ZenPosition getPosition() { + return position; + } + + /** + * Returns the string value of this token. + * + * @return token value + */ + public String getValue() { + return value; + } + + /** + * Returns the token type of this token. + * + * @return token type + */ + public int getType() { + return type; + } + + @Override + public String toString() { + return position.getLine() + ":" + position.getLineOffset() + " (" + type + ") " + value; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/TokenException.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/TokenException.java new file mode 100644 index 0000000..8207d22 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/TokenException.java @@ -0,0 +1,17 @@ +/* + * This file is subject to the license.txt file in the main folder + * of this project. + */ + +package stanhebben.zenscript.parser; + +/** + * Thrown when an exception occurs while tokening a character stream. + * + * @author Stan Hebben + */ +public class TokenException extends RuntimeException { + public TokenException(int line, int lineOffset, char value) { + super("Invalid character at " + line + ":" + lineOffset + " - " + value); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/TokenStream.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/TokenStream.java new file mode 100644 index 0000000..8e47417 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/TokenStream.java @@ -0,0 +1,260 @@ +/* + * This file is subject to the license.txt file in the main folder + * of this project. + */ + +package stanhebben.zenscript.parser; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Stack; +import stanhebben.zenscript.ZenParsedFile; +import stanhebben.zenscript.util.ZenPosition; + +/** + * Represents a token stream. A token stream reads characters from a reader and + * presents it as a series of tokens. + * + * Token classes with a negative class are considered to be whitespace. + * + * @author Stan Hebben + */ +public class TokenStream implements Iterator { + private ZenParsedFile file; + + private CountingReader reader; + private CompiledDFA dfa; + private Token next; + private int nextChar; + private int line; + private int lineOffset; + + private int tokenMemoryOffset; + private LinkedList tokenMemory; + private Stack marks; + private int tokenMemoryCurrent; + + /** + * Creates a token stream using the specified reader and DFA. + * + * @param reader reader to read characters from + * @param dfa DFA to tokenize the stream + * @throws IOException + */ + public TokenStream(Reader reader, CompiledDFA dfa) throws IOException { + tokenMemoryOffset = 0; + tokenMemoryCurrent = 0; + tokenMemory = new LinkedList(); + marks = new Stack(); + + this.reader = new CountingReader(reader); + this.dfa = dfa; + nextChar = this.reader.read(); + line = 1; + lineOffset = 1; + advance(); + } + + /** + * Creates a token stream which reads data from the specified string. + * + * @param data data to read + * @param dfa DFA to tokenize the stream + * @throws IOException + */ + public TokenStream(String data, CompiledDFA dfa) throws IOException { + this(new StringReader(data), dfa); + } + + public void setFile(ZenParsedFile file) { + this.file = file; + } + + public ZenParsedFile getFile() { + return file; + } + + public Token peek() { + if (tokenMemoryCurrent < tokenMemoryOffset + tokenMemory.size()) { + return tokenMemory.get((tokenMemoryCurrent) - tokenMemoryOffset); + } else { + return next; + } + } + + public boolean isNext(int type) { + return peek().getType() == type; + } + + public Token optional(int type) { + if (peek() != null && peek().getType() == type) { + return next(); + } else { + return null; + } + } + + public Token required(int type, String error) { + Token t = peek(); + if (t != null && t.getType() == type) { + return next(); + } else { + throw new ParseException(t, error); + } + } + + // LL(*) ability + + /** + * Pushes a mark on the mark stack. + */ + public void pushMark() { + marks.push(tokenMemoryCurrent); + } + + /** + * Pops a mark from the mark stack without reset. + */ + public void popMark() { + marks.pop(); + + if (marks.isEmpty()) { + tokenMemoryOffset = tokenMemoryCurrent; + tokenMemory.clear(); + } + } + + /** + * Pops a mark from the mark stack and resets the stream's position to it + */ + public void reset() { + tokenMemoryCurrent = marks.pop(); + } + + + //////////////////////////// + // Iterator implementation + //////////////////////////// + + public boolean hasNext() { + return next != null; + } + + public Token next() { + if (tokenMemoryCurrent < tokenMemoryOffset + tokenMemory.size()) { + return tokenMemory.get((tokenMemoryCurrent++) - tokenMemoryOffset); + } else { + Token result = next; + + if (marks.isEmpty()) { + tokenMemoryOffset++; + } else { + tokenMemory.add(result); + } + tokenMemoryCurrent++; + + advance(); + return result; + } + } + + public void remove() { + throw new UnsupportedOperationException("Not supported."); + } + + ////////////////////// + // Protected methods + ////////////////////// + + /** + * Can be implemented by subclasses to post-process tokens. + * + * @param token + * @return processed token + */ + protected Token process(Token token) { + return token; + } + + //////////////////// + // Private methods + //////////////////// + + /** + * Advances to the next non - whitespace token. + */ + private void advance() { + do { + advanceToken(); + } while (next != null && next.getType() < 0); + } + + /** + * Advances to the next token. + */ + private void advanceToken() { + if (nextChar < 0) { + next = null; + return; + } + try { + int state = 0; + StringBuilder value = new StringBuilder(); + int tLine = line; + int tLineOffset = lineOffset; + while (dfa.transitions[state].containsKey(nextChar)) { + value.append((char)nextChar); + state = dfa.transitions[state].get(nextChar); + line = reader.line; + lineOffset = reader.lineOffset; + nextChar = reader.read(); + } + if (dfa.finals[state] != CompiledDFA.NOFINAL) { + if (state == 0) throw new TokenException(line, lineOffset, (char)nextChar); + next = process(new Token(value.toString(), dfa.finals[state], new ZenPosition(file, tLine, tLineOffset))); + } else { + throw new TokenException(line, lineOffset, (char)nextChar); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + ////////////////////////// + // Private inner classes + ////////////////////////// + + /** + * Keeps a line and line offset count. + */ + private class CountingReader { + private int line; + private int lineOffset; + private Reader reader; + private boolean eof; + + public CountingReader(Reader reader) { + this.reader = reader; + line = 1; + lineOffset = 1; + } + + public int read() throws IOException { + int ch = reader.read(); + if (ch == -1) { + eof = true; + return ch; + } + if (ch == '\n') { + line++; + lineOffset = 1; + } else { + lineOffset++; + } + return ch; + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpression.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpression.java new file mode 100644 index 0000000..79696c3 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpression.java @@ -0,0 +1,440 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import java.util.ArrayList; +import java.util.List; +import stanhebben.zenscript.ZenParser; +import static stanhebben.zenscript.ZenParser.*; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.definitions.ParsedFunctionArgument; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionFloat; +import stanhebben.zenscript.expression.ExpressionFunction; +import stanhebben.zenscript.expression.ExpressionInt; +import stanhebben.zenscript.expression.ExpressionString; +import stanhebben.zenscript.parser.ParseException; +import stanhebben.zenscript.parser.Token; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.statements.Statement; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeAny; +import stanhebben.zenscript.type.ZenTypeDouble; +import stanhebben.zenscript.type.ZenTypeInt; +import static stanhebben.zenscript.util.StringUtil.unescapeString; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public abstract class ParsedExpression { + public static ParsedExpression read(ZenParser parser, IEnvironmentGlobal environment) { + return readAssignExpression(parser, environment); + } + + private static ParsedExpression readAssignExpression(ZenParser parser, IEnvironmentGlobal environment) { + ZenPosition position = parser.peek().getPosition(); + + ParsedExpression left = readConditionalExpression(position, parser, environment); + switch (parser.peek() == null ? -1 : parser.peek().getType()) { + case T_ASSIGN: + parser.next(); + return new ParsedExpressionAssign(position, left, readAssignExpression(parser, environment)); + case T_PLUSASSIGN: + parser.next(); + return new ParsedExpressionOpAssign(position, left, readAssignExpression(parser, environment), OperatorType.ADD); + case T_MINUSASSIGN: + parser.next(); + return new ParsedExpressionOpAssign(position, left, readAssignExpression(parser, environment), OperatorType.SUB); + case T_TILDEASSIGN: + parser.next(); + return new ParsedExpressionOpAssign(position, left, readAssignExpression(parser, environment), OperatorType.CAT); + case T_MULASSIGN: + parser.next(); + return new ParsedExpressionOpAssign(position, left, readAssignExpression(parser, environment), OperatorType.MUL); + case T_DIVASSIGN: + parser.next(); + return new ParsedExpressionOpAssign(position, left, readAssignExpression(parser, environment), OperatorType.DIV); + case T_MODASSIGN: + parser.next(); + return new ParsedExpressionOpAssign(position, left, readAssignExpression(parser, environment), OperatorType.MOD); + case T_ORASSIGN: + parser.next(); + return new ParsedExpressionOpAssign(position, left, readAssignExpression(parser, environment), OperatorType.OR); + case T_ANDASSIGN: + parser.next(); + return new ParsedExpressionOpAssign(position, left, readAssignExpression(parser, environment), OperatorType.AND); + case T_XORASSIGN: + parser.next(); + return new ParsedExpressionOpAssign(position, left, readAssignExpression(parser, environment), OperatorType.XOR); + } + + return left; + } + + private static ParsedExpression readConditionalExpression(ZenPosition position, ZenParser parser, IEnvironmentGlobal environment) { + ParsedExpression left = readOrOrExpression(position, parser, environment); + + if (parser.optional(T_QUEST) != null) { + ParsedExpression onIf = readOrOrExpression(parser.peek().getPosition(), parser, environment); + parser.required(T_COLON, ": expected"); + ParsedExpression onElse = readConditionalExpression(parser.peek().getPosition(), parser, environment); + return new ParsedExpressionConditional(position, left, onIf, onElse); + } + + return left; + } + + private static ParsedExpression readOrOrExpression(ZenPosition position, ZenParser parser, IEnvironmentGlobal environment) { + ParsedExpression left = readAndAndExpression(position, parser, environment); + + while (parser.optional(T_OR2) != null) { + ParsedExpression right = readAndAndExpression(parser.peek().getPosition(), parser, environment); + left = new ParsedExpressionOrOr(position, left, right); + } + return left; + } + + private static ParsedExpression readAndAndExpression(ZenPosition position, ZenParser parser, IEnvironmentGlobal environment) { + ParsedExpression left = readOrExpression(position, parser, environment); + + while (parser.optional(T_AND2) != null) { + ParsedExpression right = readOrExpression(parser.peek().getPosition(), parser, environment); + left = new ParsedExpressionAndAnd(position, left, right); + } + return left; + } + + private static ParsedExpression readOrExpression(ZenPosition position, ZenParser parser, IEnvironmentGlobal environment) { + ParsedExpression left = readXorExpression(position, parser, environment); + + while (parser.optional(T_OR) != null) { + ParsedExpression right = readXorExpression(parser.peek().getPosition(), parser, environment); + left = new ParsedExpressionBinary(position, left, right, OperatorType.OR); + } + return left; + } + + private static ParsedExpression readXorExpression(ZenPosition position, ZenParser parser, IEnvironmentGlobal environment) { + ParsedExpression left = readAndExpression(position, parser, environment); + + while (parser.optional(T_XOR) != null) { + ParsedExpression right = readAndExpression(parser.peek().getPosition(), parser, environment); + left = new ParsedExpressionBinary(position, left, right, OperatorType.XOR); + } + return left; + } + + private static ParsedExpression readAndExpression(ZenPosition position, ZenParser parser, IEnvironmentGlobal environment) { + ParsedExpression left = readCompareExpression(position, parser, environment); + + while (parser.optional(T_AND) != null) { + ParsedExpression right = readCompareExpression(parser.peek().getPosition(), parser, environment); + left = new ParsedExpressionBinary(position, left, right, OperatorType.AND); + } + return left; + } + + private static ParsedExpression readCompareExpression(ZenPosition position, ZenParser parser, IEnvironmentGlobal environment) { + ParsedExpression left = readAddExpression(position, parser, environment); + + switch (parser.peek() == null ? -1 : parser.peek().getType()) { + case T_EQ: { + parser.next(); + ParsedExpression right = readAddExpression(parser.peek().getPosition(), parser, environment); + return new ParsedExpressionCompare(position, left, right, CompareType.EQ); + } + case T_NOTEQ: { + parser.next(); + ParsedExpression right = readAddExpression(parser.peek().getPosition(), parser, environment); + return new ParsedExpressionCompare(position, left, right, CompareType.NE); + } + case T_LT: { + parser.next(); + ParsedExpression right = readAddExpression(parser.peek().getPosition(), parser, environment); + return new ParsedExpressionCompare(position, left, right, CompareType.LT); + } + case T_LTEQ: { + parser.next(); + ParsedExpression right = readAddExpression(parser.peek().getPosition(), parser, environment); + return new ParsedExpressionCompare(position, left, right, CompareType.LE); + } + case T_GT: { + parser.next(); + ParsedExpression right = readAddExpression(parser.peek().getPosition(), parser, environment); + return new ParsedExpressionCompare(position, left, right, CompareType.GT); + } + case T_GTEQ: { + parser.next(); + ParsedExpression right = readAddExpression(parser.peek().getPosition(), parser, environment); + return new ParsedExpressionCompare(position, left, right, CompareType.GE); + } + case T_IN: { + parser.next(); + ParsedExpression right = readAddExpression(parser.peek().getPosition(), parser, environment); + return new ParsedExpressionBinary(position, left, right, OperatorType.CONTAINS); + } + } + + return left; + } + + private static ParsedExpression readAddExpression(ZenPosition position, ZenParser parser, IEnvironmentGlobal environment) { + ParsedExpression left = readMulExpression(position, parser, environment); + + while (true) { + if (parser.optional(T_PLUS) != null) { + ParsedExpression right = readMulExpression(parser.peek().getPosition(), parser, environment); + left = new ParsedExpressionBinary(position, left, right, OperatorType.ADD); + } else if (parser.optional(T_MINUS) != null) { + ParsedExpression right = readMulExpression(parser.peek().getPosition(), parser, environment); + left = new ParsedExpressionBinary(position, left, right, OperatorType.SUB); + } else if (parser.optional(T_TILDE) != null) { + ParsedExpression right = readMulExpression(parser.peek().getPosition(), parser, environment); + left = new ParsedExpressionBinary(position, left, right, OperatorType.CAT); + } else { + break; + } + } + return left; + } + + private static ParsedExpression readMulExpression(ZenPosition position, ZenParser parser, IEnvironmentGlobal environment) { + ParsedExpression left = readUnaryExpression(position, parser, environment); + + while (true) { + if (parser.optional(T_MUL) != null) { + ParsedExpression right = readUnaryExpression(parser.peek().getPosition(), parser, environment); + left = new ParsedExpressionBinary(position, left, right, OperatorType.MUL); + } else if (parser.optional(T_DIV) != null) { + ParsedExpression right = readUnaryExpression(parser.peek().getPosition(), parser, environment); + left = new ParsedExpressionBinary(position, left, right, OperatorType.DIV); + } else if (parser.optional(T_MOD) != null) { + ParsedExpression right = readUnaryExpression(parser.peek().getPosition(), parser, environment); + left = new ParsedExpressionBinary(position, left, right, OperatorType.MOD); + } else { + break; + } + } + + return left; + } + + private static ParsedExpression readUnaryExpression(ZenPosition position, ZenParser parser, IEnvironmentGlobal environment) { + switch (parser.peek().getType()) { + case T_NOT: + parser.next(); + return new ParsedExpressionUnary( + position, + readUnaryExpression(parser.peek().getPosition(), parser, environment), + OperatorType.NOT); + case T_MINUS: + parser.next(); + return new ParsedExpressionUnary( + position, + readUnaryExpression(parser.peek().getPosition(), parser, environment), + OperatorType.NEG); + default: + return readPostfixExpression(position, parser, environment); + } + } + + private static ParsedExpression readPostfixExpression(ZenPosition position, ZenParser parser, IEnvironmentGlobal environment) { + ParsedExpression base = readPrimaryExpression(position, parser, environment); + + while (true) { + if (parser.optional(T_DOT) != null) { + Token indexString = parser.optional(T_ID); + if (indexString != null) { + base = new ParsedExpressionMember(position, base, indexString.getValue()); + } else { + Token indexString2 = parser.optional(T_STRING); + if (indexString2 != null) { + base = new ParsedExpressionMember(position, base, unescapeString(indexString2.getValue())); + } else { + Token last = parser.next(); + throw new ParseException(last, "Invalid expression, last token: " + last.getValue()); + } + } + } else if (parser.optional(T_DOT2) != null) { + //ParsedExpression to = readAssignExpression(parser, errors); + //base = base.range(position, errors, to); + throw new RuntimeException("not yet supported"); + } else if (parser.optional(T_SQBROPEN) != null) { + ParsedExpression index = readAssignExpression(parser, environment); + base = new ParsedExpressionIndex(position, base, index); + parser.required(T_SQBRCLOSE, "] expected"); + } else if (parser.optional(T_BROPEN) != null) { + ArrayList arguments = new ArrayList(); + if (parser.optional(T_BRCLOSE) == null) { + arguments.add(readAssignExpression(parser, environment)); + while (parser.optional(T_COMMA) != null) { + arguments.add(readAssignExpression(parser, environment)); + } + parser.required(T_BRCLOSE, ") expected"); + } + base = new ParsedExpressionCall(position, base, arguments); + } else if (parser.optional(T_AS) != null) { + ZenType type = ZenType.read(parser, environment); + base = new ParsedExpressionCast(position, base, type); + } else { + break; + } + } + + return base; + } + + private static ParsedExpression readPrimaryExpression(ZenPosition position, ZenParser parser, IEnvironmentGlobal environment) { + switch (parser.peek().getType()) { + case T_INTVALUE: + return new ParsedExpressionValue( + position, + new ExpressionInt(position, Long.parseLong(parser.next().getValue()), ZenTypeInt.INSTANCE)); + case T_FLOATVALUE: + return new ParsedExpressionValue( + position, + new ExpressionFloat(position, Double.parseDouble(parser.next().getValue()), ZenTypeDouble.INSTANCE)); + case T_STRINGVALUE: + return new ParsedExpressionValue( + position, + new ExpressionString(position, unescapeString(parser.next().getValue()))); + /*case T_DOLLAR: { + Expression result = new ExpressionDollar(position); + if (parser.isNext(T_STRINGVALUE)) { + return new ExpressionIndexString(position, result, unescapeString(parser.next().getValue())); + } + return result; + }*/ + case T_ID: + return new ParsedExpressionVariable(position, parser.next().getValue()); + // TODO: implement function values + case T_FUNCTION: + // function (argname, argname, ...) { ...contents... } + parser.next(); + parser.required(T_BROPEN, "( expected"); + + List arguments = new ArrayList(); + if (parser.optional(T_BRCLOSE) == null) { + do { + String name = parser.required(T_ID, "identifier expected").getValue(); + ZenType type = ZenTypeAny.INSTANCE; + + if (parser.optional(T_AS) != null) { + type = ZenType.read(parser, environment); + } + + arguments.add(new ParsedFunctionArgument(name, type)); + } while (parser.optional(T_COMMA) != null); + + parser.required(T_BRCLOSE, ") expected"); + } + + ZenType returnType = ZenTypeAny.INSTANCE; + if (parser.optional(T_AS) != null) { + returnType = ZenType.read(parser, environment); + } + + parser.required(T_AOPEN, "{ expected"); + + Statement[] statements; + if (parser.optional(T_ACLOSE) != null) { + statements = new Statement[0]; + } else { + ArrayList statementsAL = new ArrayList(); + + while (parser.optional(T_ACLOSE) == null) { + statementsAL.add(Statement.read(parser, environment)); + } + statements = statementsAL.toArray(new Statement[statementsAL.size()]); + } + return new ParsedExpressionValue(position, new ExpressionFunction(position, arguments, returnType, statements)); + case T_LT: { + Token start = parser.next(); + List tokens = new ArrayList(); + Token next = parser.next(); + while (next.getType() != ZenParser.T_GT) { + tokens.add(next); + next = parser.next(); + } + return new ParsedExpressionValue( + start.getPosition(), + parser.getEnvironment().getBracketed(tokens).instance(start.getPosition())); + } + case T_SQBROPEN: { + parser.next(); + List contents = new ArrayList(); + if (parser.optional(T_SQBRCLOSE) == null) { + while (parser.optional(T_SQBRCLOSE) == null) { + contents.add(readAssignExpression(parser, environment)); + if (parser.optional(T_COMMA) == null) { + parser.required(T_SQBRCLOSE, "] or , expected"); + break; + } + } + } + return new ParsedExpressionArray(position, contents); + } + case T_AOPEN: { + parser.next(); + + List keys = new ArrayList(); + List values = new ArrayList(); + if (parser.optional(T_ACLOSE) == null) { + while (parser.optional(T_ACLOSE) == null) { + keys.add(readAssignExpression(parser, environment)); + parser.required(T_COLON, ": expected"); + values.add(readAssignExpression(parser, environment)); + + if (parser.optional(T_COMMA) == null) { + parser.required(T_ACLOSE, "} or , expected"); + break; + } + } + + return new ParsedExpressionTable(position, keys, values); + } + } + case T_TRUE: + parser.next(); + return new ParsedExpressionBool(position, true); + case T_FALSE: + parser.next(); + return new ParsedExpressionBool(position, false); + case T_NULL: + parser.next(); + return new ParsedExpressionNull(position); + case T_BROPEN: + parser.next(); + ParsedExpression result = readAssignExpression(parser, environment); + parser.required(T_BRCLOSE, ") expected"); + return result; + default: + Token last = parser.next(); + throw new ParseException(last, "Invalid expression, last token: " + last.getValue()); + } + } + private final ZenPosition position; + + public ParsedExpression(ZenPosition position) { + this.position = position; + } + + public ZenPosition getPosition() { + return position; + } + + public abstract IPartialExpression compile(IEnvironmentMethod environment); + + public Expression compileKey(IEnvironmentMethod environment) { + return compile(environment).eval(environment); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionAndAnd.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionAndAnd.java new file mode 100644 index 0000000..128f725 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionAndAnd.java @@ -0,0 +1,36 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.ExpressionOrOr; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionAndAnd extends ParsedExpression { + private final ParsedExpression left; + private final ParsedExpression right; + + public ParsedExpressionAndAnd(ZenPosition position, ParsedExpression left, ParsedExpression right) { + super(position); + + this.left = left; + this.right = right; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + return new ExpressionOrOr( + getPosition(), + left.compile(environment).eval(environment), + right.compile(environment).eval(environment)); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionArray.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionArray.java new file mode 100644 index 0000000..693cb4c --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionArray.java @@ -0,0 +1,46 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import java.util.List; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionArray; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionArray extends ParsedExpression { + private final List contents; + + public ParsedExpressionArray(ZenPosition position, List contents) { + super(position); + + this.contents = contents; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + Expression[] cContents = new Expression[contents.size()]; + for (int i = 0; i < contents.size(); i++) { + cContents[i] = contents.get(i).compile(environment).eval(environment); + } + return new ExpressionArray(getPosition(), cContents); + } + + @Override + public Expression compileKey(IEnvironmentMethod environment) { + if (contents.size() == 1 && contents.get(0) instanceof ParsedExpressionVariable) { + return contents.get(0).compile(environment).eval(environment); + } else { + return compile(environment).eval(environment); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionAssign.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionAssign.java new file mode 100644 index 0000000..6f3b5a4 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionAssign.java @@ -0,0 +1,35 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionAssign extends ParsedExpression { + private final ParsedExpression left; + private final ParsedExpression right; + + public ParsedExpressionAssign(ZenPosition position, ParsedExpression left, ParsedExpression right) { + super(position); + + this.left = left; + this.right = right; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + IPartialExpression cLeft = left.compile(environment); + return cLeft + .assign(getPosition(), environment, + right.compile(environment).eval(environment)); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionBinary.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionBinary.java new file mode 100644 index 0000000..41c234a --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionBinary.java @@ -0,0 +1,38 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionBinary extends ParsedExpression { + private final ParsedExpression left; + private final ParsedExpression right; + private final OperatorType operator; + + public ParsedExpressionBinary(ZenPosition position, ParsedExpression left, ParsedExpression right, OperatorType operator) { + super(position); + + this.left = left; + this.right = right; + this.operator = operator; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + Expression cLeft = left.compile(environment).eval(environment); + Expression cRight = right.compile(environment).eval(environment); + return cLeft.getType().binary(getPosition(), environment, cLeft, cRight, operator); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionBool.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionBool.java new file mode 100644 index 0000000..1daccc4 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionBool.java @@ -0,0 +1,31 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.ExpressionBool; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionBool extends ParsedExpression { + private final boolean value; + + public ParsedExpressionBool(ZenPosition position, boolean value) { + super(position); + + this.value = value; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + return new ExpressionBool(getPosition(), value); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionCall.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionCall.java new file mode 100644 index 0000000..da095be --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionCall.java @@ -0,0 +1,41 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import java.util.List; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionCall extends ParsedExpression { + private final ParsedExpression receiver; + private final List arguments; + + public ParsedExpressionCall(ZenPosition position, ParsedExpression receiver, List arguments) { + super(position); + + this.receiver = receiver; + this.arguments = arguments; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + IPartialExpression cReceiver = receiver.compile(environment); + Expression[] cArguments = new Expression[arguments.size()]; + for (int i = 0; i < cArguments.length; i++) { + IPartialExpression cArgument = arguments.get(i).compile(environment); + cArguments[i] = cArgument.eval(environment); + } + + return cReceiver.call(getPosition(), environment, cArguments); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionCast.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionCast.java new file mode 100644 index 0000000..ab42021 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionCast.java @@ -0,0 +1,36 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionCast extends ParsedExpression { + private final ParsedExpression value; + private final ZenType type; + + public ParsedExpressionCast(ZenPosition position, ParsedExpression value, ZenType type) { + super(position); + + this.value = value; + this.type = type; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + return value + .compile(environment) + .eval(environment) + .cast(getPosition(), environment, type); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionCompare.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionCompare.java new file mode 100644 index 0000000..a91cc9a --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionCompare.java @@ -0,0 +1,42 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionCompare extends ParsedExpression { + private final ParsedExpression left; + private final ParsedExpression right; + private final CompareType type; + + public ParsedExpressionCompare( + ZenPosition position, + ParsedExpression left, + ParsedExpression right, + CompareType type) { + super(position); + + this.left = left; + this.right = right; + this.type = type; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + Expression cLeft = left.compile(environment).eval(environment); + Expression cRight = right.compile(environment).eval(environment); + return cLeft.getType().compare(getPosition(), environment, cLeft, cRight, type); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionConditional.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionConditional.java new file mode 100644 index 0000000..15e19e8 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionConditional.java @@ -0,0 +1,39 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.ExpressionConditional; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionConditional extends ParsedExpression { + private final ParsedExpression condition; + private final ParsedExpression ifThen; + private final ParsedExpression ifElse; + + public ParsedExpressionConditional(ZenPosition position, ParsedExpression condition, ParsedExpression ifThen, ParsedExpression ifElse) { + super(position); + + this.condition = condition; + this.ifThen = ifThen; + this.ifElse = ifElse; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + return new ExpressionConditional( + getPosition(), + condition.compile(environment).eval(environment), + ifThen.compile(environment).eval(environment), + ifElse.compile(environment).eval(environment)); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionIndex.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionIndex.java new file mode 100644 index 0000000..4e13f8d --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionIndex.java @@ -0,0 +1,36 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionIndex extends ParsedExpression { + private final ParsedExpression value; + private final ParsedExpression index; + + public ParsedExpressionIndex(ZenPosition position, ParsedExpression value, ParsedExpression index) { + super(position); + + this.value = value; + this.index = index; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + Expression cValue = value.compile(environment).eval(environment); + Expression cIndex = index.compile(environment).eval(environment); + return cValue.getType().binary(getPosition(), environment, cValue, cIndex, OperatorType.INDEXGET); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionIndexSet.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionIndexSet.java new file mode 100644 index 0000000..82cf38a --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionIndexSet.java @@ -0,0 +1,39 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionIndexSet extends ParsedExpression { + private final ParsedExpression value; + private final ParsedExpression index; + private final ParsedExpression setValue; + + public ParsedExpressionIndexSet(ZenPosition position, ParsedExpression value, ParsedExpression index, ParsedExpression setValue) { + super(position); + + this.value = value; + this.index = index; + this.setValue = setValue; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + Expression cValue = value.compile(environment).eval(environment); + Expression cIndex = index.compile(environment).eval(environment); + Expression cSetValue = setValue.compile(environment).eval(environment); + return cValue.getType().trinary(getPosition(), environment, cValue, cIndex, cSetValue,OperatorType.INDEXSET); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionInvalid.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionInvalid.java new file mode 100644 index 0000000..c7223d9 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionInvalid.java @@ -0,0 +1,27 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionInvalid extends ParsedExpression { + public ParsedExpressionInvalid(ZenPosition position) { + super(position); + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + return new ExpressionInvalid(getPosition()); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionMember.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionMember.java new file mode 100644 index 0000000..18c4c59 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionMember.java @@ -0,0 +1,32 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionMember extends ParsedExpression { + private final ParsedExpression value; + private final String member; + + public ParsedExpressionMember(ZenPosition position, ParsedExpression value, String member) { + super(position); + + this.value = value; + this.member = member; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + return value.compile(environment).getMember(getPosition(), environment, member); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionNull.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionNull.java new file mode 100644 index 0000000..7f1d8c6 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionNull.java @@ -0,0 +1,27 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.ExpressionNull; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionNull extends ParsedExpression { + public ParsedExpressionNull(ZenPosition position) { + super(position); + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + return new ExpressionNull(getPosition()); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionOpAssign.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionOpAssign.java new file mode 100644 index 0000000..026daf9 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionOpAssign.java @@ -0,0 +1,41 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionOpAssign extends ParsedExpression { + private final ParsedExpression left; + private final ParsedExpression right; + private final OperatorType operator; + + public ParsedExpressionOpAssign(ZenPosition position, ParsedExpression left, ParsedExpression right, OperatorType operator) { + super(position); + + this.left = left; + this.right = right; + this.operator = operator; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + Expression cLeft = left.compile(environment).eval(environment); + Expression cRight = right.compile(environment).eval(environment); + + Expression value = cLeft.getType().binary(getPosition(), environment, cLeft, cRight, operator); + + return left.compile(environment).assign(getPosition(), environment, value); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionOrOr.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionOrOr.java new file mode 100644 index 0000000..6f667ce --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionOrOr.java @@ -0,0 +1,36 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.ExpressionOrOr; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionOrOr extends ParsedExpression { + private final ParsedExpression left; + private final ParsedExpression right; + + public ParsedExpressionOrOr(ZenPosition position, ParsedExpression left, ParsedExpression right) { + super(position); + + this.left = left; + this.right = right; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + return new ExpressionOrOr( + getPosition(), + left.compile(environment).eval(environment), + right.compile(environment).eval(environment)); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionTable.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionTable.java new file mode 100644 index 0000000..0d0f683 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionTable.java @@ -0,0 +1,46 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import java.util.List; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionTable; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionTable extends ParsedExpression { + private final List keys; + private final List values; + + public ParsedExpressionTable( + ZenPosition position, + List keys, + List values) { + super(position); + + this.keys = keys; + this.values = values; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + Expression[] cKeys = new Expression[keys.size()]; + Expression[] cValues = new Expression[values.size()]; + + for (int i = 0; i < keys.size(); i++) { + cKeys[i] = keys.get(i).compileKey(environment); + cValues[i] = values.get(i).compile(environment).eval(environment); + } + + return new ExpressionTable(getPosition(), cKeys, cValues); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionUnary.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionUnary.java new file mode 100644 index 0000000..83bafc8 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionUnary.java @@ -0,0 +1,35 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionUnary extends ParsedExpression { + private final ParsedExpression value; + private final OperatorType operator; + + public ParsedExpressionUnary(ZenPosition position, ParsedExpression value, OperatorType operator) { + super(position); + + this.value = value; + this.operator = operator; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + Expression cValue = value.compile(environment).eval(environment); + return cValue.getType().unary(getPosition(), environment, cValue, operator); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionValue.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionValue.java new file mode 100644 index 0000000..17deef5 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionValue.java @@ -0,0 +1,30 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionValue extends ParsedExpression { + private final IPartialExpression value; + + public ParsedExpressionValue(ZenPosition position, IPartialExpression value) { + super(position); + + this.value = value; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + return value; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionVariable.java b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionVariable.java new file mode 100644 index 0000000..1b7dfdd --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/parser/expression/ParsedExpressionVariable.java @@ -0,0 +1,44 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.parser.expression; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.ExpressionString; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ParsedExpressionVariable extends ParsedExpression { + private final String name; + + public ParsedExpressionVariable(ZenPosition position, String name) { + super(position); + + this.name = name; + } + + @Override + public IPartialExpression compile(IEnvironmentMethod environment) { + IPartialExpression result = environment.getValue(name, getPosition()); + if (result == null) { + environment.error(getPosition(), "could not find " + name); + return new ExpressionInvalid(getPosition()); + } else { + return result; + } + } + + @Override + public Expression compileKey(IEnvironmentMethod environment) { + return new ExpressionString(getPosition(), name); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/statements/Statement.java b/ZenScript/src/main/java/stanhebben/zenscript/statements/Statement.java new file mode 100644 index 0000000..d34f9c6 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/statements/Statement.java @@ -0,0 +1,110 @@ +package stanhebben.zenscript.statements; + +import java.util.ArrayList; +import java.util.List; + +import stanhebben.zenscript.ZenParser; +import static stanhebben.zenscript.ZenParser.*; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.parser.Token; +import stanhebben.zenscript.parser.expression.ParsedExpression; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +public abstract class Statement { + public static Statement read(ZenParser parser, IEnvironmentGlobal environment) { + Token next = parser.peek(); + switch (next.getType()) { + case T_AOPEN: { + Token t = parser.next(); + ArrayList statements = new ArrayList(); + while (parser.optional(T_ACLOSE) == null) { + statements.add(read(parser, environment)); + } + return new StatementBlock(t.getPosition(), statements); + } + case T_RETURN: { + parser.next(); + ParsedExpression expression = ParsedExpression.read(parser, environment); + parser.required(T_SEMICOLON, "; expected"); + return new StatementReturn(next.getPosition(), expression); + } + case T_VAR: + case T_VAL: { + Token start = parser.next(); + String name = parser.required(T_ID, "identifier expected").getValue(); + + ZenType type = null; + ParsedExpression initializer = null; + if (parser.optional(T_AS) != null) { + type = ZenType.read(parser, environment); + } + if (parser.optional(T_ASSIGN) != null) { + initializer = ParsedExpression.read(parser, environment); + } + parser.required(T_SEMICOLON, "; expected"); + return new StatementVar(start.getPosition(), name, type, initializer, start.getType() == T_VAL); + } + case T_IF: { + Token t = parser.next(); + ParsedExpression expression = ParsedExpression.read(parser, environment); + Statement onIf = read(parser, environment); + Statement onElse = null; + if (parser.optional(T_ELSE) != null) { + onElse = read(parser, environment); + } + return new StatementIf(t.getPosition(), expression, onIf, onElse); + } + case T_FOR: { + Token t = parser.next(); + String name = parser.required(T_ID, "identifier expected").getValue(); + List names = new ArrayList(); + names.add(name); + + while (parser.optional(T_COMMA) != null) { + names.add(parser.required(T_ID, "identifier expected").getValue()); + } + + parser.required(T_COLON, ": expected"); + ParsedExpression source = ParsedExpression.read(parser, environment); + Statement content = read(parser, environment); + return new StatementForeach( + t.getPosition(), + names.toArray(new String[names.size()]), + source, + content); + } + case T_VERSION: { + Token t = parser.next(); + parser.required(T_INTVALUE, "integer expected"); + + //MineTweaker.logWarning("The version statement is no longer necessary"); + + parser.required(T_SEMICOLON, "; expected"); + return new StatementNull(t.getPosition()); + } + } + + ZenPosition position = parser.peek().getPosition(); + StatementExpression result = new StatementExpression(position, ParsedExpression.read(parser, environment)); + parser.required(T_SEMICOLON, "; expected"); + return result; + } + + private final ZenPosition position; + + public Statement(ZenPosition position) { + this.position = position; + } + + public ZenPosition getPosition() { + return position; + } + + public boolean isReturn() { + return false; + } + + public abstract void compile(IEnvironmentMethod environment); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementBlock.java b/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementBlock.java new file mode 100644 index 0000000..babcd58 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementBlock.java @@ -0,0 +1,27 @@ +package stanhebben.zenscript.statements; + +import java.util.List; +import stanhebben.zenscript.compiler.EnvironmentScope; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.util.ZenPosition; + +public class StatementBlock extends Statement { + private final List statements; + + public StatementBlock(ZenPosition position, List statements) { + super(position); + + this.statements = statements; + } + + @Override + public void compile(IEnvironmentMethod environment) { + IEnvironmentMethod local = new EnvironmentScope(environment); + for (Statement statement : statements) { + statement.compile(local); + if (statement.isReturn()) { + return; + } + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementExpression.java b/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementExpression.java new file mode 100644 index 0000000..e678f66 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementExpression.java @@ -0,0 +1,23 @@ +package stanhebben.zenscript.statements; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.parser.expression.ParsedExpression; +import stanhebben.zenscript.util.ZenPosition; + +public class StatementExpression extends Statement { + private final ParsedExpression expression; + + public StatementExpression(ZenPosition position, ParsedExpression expression) { + super(position); + + this.expression = expression; + } + + @Override + public void compile(IEnvironmentMethod environment) { + environment.getOutput().position(getPosition()); + expression.compile(environment) + .eval(environment) + .compile(false, environment); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementForeach.java b/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementForeach.java new file mode 100644 index 0000000..b18fe64 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementForeach.java @@ -0,0 +1,60 @@ +package stanhebben.zenscript.statements; + +import org.objectweb.asm.Label; +import stanhebben.zenscript.compiler.EnvironmentScope; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.parser.expression.ParsedExpression; +import stanhebben.zenscript.type.IZenIterator; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.symbols.SymbolLocal; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +public class StatementForeach extends Statement { + private final String[] varnames; + private final ParsedExpression list; + private final Statement body; + + public StatementForeach(ZenPosition position, String[] varnames, ParsedExpression list, Statement body) { + super(position); + + this.varnames = varnames; + this.list = list; + this.body = body; + } + + @Override + public void compile(IEnvironmentMethod environment) { + Expression cList = list.compile(environment).eval(environment); + ZenType listType = cList.getType(); + + IZenIterator iterator = listType.makeIterator(varnames.length, environment); + if (iterator == null) { + environment.error(getPosition(), "No iterator with " + varnames.length + " variables"); + return; + } + + MethodOutput methodOutput = environment.getOutput(); + environment.getOutput().position(getPosition()); + + IEnvironmentMethod local = new EnvironmentScope(environment); + int[] localVariables = new int[varnames.length]; + for (int i = 0; i < localVariables.length; i++) { + localVariables[i] = methodOutput.local(iterator.getType(i).toASMType()); + local.putValue(varnames[i], new SymbolLocal(iterator.getType(i), true)); + } + + cList.compile(true, environment); + iterator.compileStart(localVariables); + + Label repeat = new Label(); + Label exit = new Label(); + + methodOutput.label(repeat); + iterator.compilePreIterate(localVariables, exit); + body.compile(local); + iterator.compilePostIterate(localVariables, exit, repeat); + methodOutput.label(exit); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementIf.java b/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementIf.java new file mode 100644 index 0000000..44c8345 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementIf.java @@ -0,0 +1,51 @@ +package stanhebben.zenscript.statements; + +import org.objectweb.asm.Label; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.parser.expression.ParsedExpression; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeBool; +import stanhebben.zenscript.symbols.IZenNamespace; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +public class StatementIf extends Statement { + private final ParsedExpression condition; + private final Statement onThen; + private final Statement onElse; + + public StatementIf(ZenPosition position, ParsedExpression condition, Statement onThen, Statement onElse) { + super(position); + + this.condition = condition; + this.onThen = onThen; + this.onElse = onElse; + } + + @Override + public void compile(IEnvironmentMethod environment) { + environment.getOutput().position(getPosition()); + + Expression cCondition = condition.compile(environment).eval(environment); + ZenType expressionType = cCondition.getType(); + if (expressionType.canCastImplicit(ZenTypeBool.INSTANCE, environment)) { + Label labelEnd = new Label(); + Label labelElse = onElse == null ? labelEnd : new Label(); + + cCondition.compileIf(labelElse, environment); + onThen.compile(environment); + + MethodOutput methodOutput = environment.getOutput(); + if (onElse != null) { + methodOutput.goTo(labelEnd); + methodOutput.label(labelElse); + onElse.compile(environment); + } + + methodOutput.label(labelEnd); + } else { + environment.error(getPosition(), "condition is not a boolean"); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementNull.java b/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementNull.java new file mode 100644 index 0000000..c6e6eb3 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementNull.java @@ -0,0 +1,25 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.statements; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class StatementNull extends Statement { + public StatementNull(ZenPosition position) { + super(position); + } + + @Override + public void compile(IEnvironmentMethod environment) { + + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementReturn.java b/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementReturn.java new file mode 100644 index 0000000..f2c7022 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementReturn.java @@ -0,0 +1,41 @@ +package stanhebben.zenscript.statements; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.parser.expression.ParsedExpression; +import stanhebben.zenscript.util.ZenPosition; + +public class StatementReturn extends Statement { + private final ParsedExpression expression; + + public StatementReturn(ZenPosition position, ParsedExpression expression) { + super(position); + + this.expression = expression; + } + + public ParsedExpression getExpression() { + return expression; + } + + @Override + public boolean isReturn() { + return false; + } + + @Override + public void compile(IEnvironmentMethod environment) { + environment.getOutput().position(getPosition()); + + if (expression == null) { + environment.getOutput().ret(); + } else { + Expression cExpression = expression.compile(environment).eval(environment); + cExpression.compile(true, environment); + + Type returnType = cExpression.getType().toASMType(); + environment.getOutput().returnType(returnType); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementVar.java b/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementVar.java new file mode 100644 index 0000000..61f1dab --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/statements/StatementVar.java @@ -0,0 +1,53 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.statements; + +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.parser.expression.ParsedExpression; +import stanhebben.zenscript.symbols.SymbolLocal; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeAny; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class StatementVar extends Statement { + private final String name; + private final ZenType type; + private final ParsedExpression initializer; + private final boolean isFinal; + + public StatementVar(ZenPosition position, String name, ZenType type, ParsedExpression initializer, boolean isFinal) { + super(position); + + this.name = name; + this.type = type; + this.initializer = initializer; + this.isFinal = isFinal; + } + + @Override + public void compile(IEnvironmentMethod environment) { + environment.getOutput().position(getPosition()); + + Expression cInitializer = initializer == null + ? null + : initializer.compile(environment).eval(environment); + ZenType cType = type == null ? (cInitializer == null ? ZenTypeAny.INSTANCE : cInitializer.getType()) : type; + SymbolLocal symbol = new SymbolLocal(cType, isFinal); + + environment.putValue(name, symbol); + + if (cInitializer != null) { + cInitializer.compile(true, environment); + environment.getOutput().store(symbol.getType().toASMType(), environment.getLocal(symbol)); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenCompileEnvironment.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenCompileEnvironment.java new file mode 100644 index 0000000..2b3b1a8 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenCompileEnvironment.java @@ -0,0 +1,31 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.symbols; + +import java.util.List; +import stanhebben.zenscript.IZenErrorLogger; +import stanhebben.zenscript.TypeExpansion; +import stanhebben.zenscript.compiler.TypeRegistry; +import stanhebben.zenscript.parser.Token; + +/** + * + * @author Stanneke + */ +public interface IZenCompileEnvironment { + public IZenErrorLogger getErrorLogger(); + + public IZenSymbol getGlobal(String name); + + public IZenSymbol getDollar(String name); + + public IZenSymbol getBracketed(List tokens); + + public TypeRegistry getTypeRegistry(); + + public TypeExpansion getExpansion(String type); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenEnvironment.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenEnvironment.java new file mode 100644 index 0000000..d74ee87 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenEnvironment.java @@ -0,0 +1,15 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.symbols; + +/** + * + * @author Stanneke + */ +public interface IZenEnvironment { + public Object getValue(byte[] key); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenNamespace.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenNamespace.java new file mode 100644 index 0000000..ba9ffcc --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenNamespace.java @@ -0,0 +1,22 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.symbols; + +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public interface IZenNamespace { + public IZenCompileEnvironment getEnvironment(); + + public IPartialExpression getValue(String name, ZenPosition position); + + public void putValue(String name, IZenSymbol value); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenSymbol.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenSymbol.java new file mode 100644 index 0000000..576d792 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/symbols/IZenSymbol.java @@ -0,0 +1,18 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.symbols; + +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public interface IZenSymbol { + public IPartialExpression instance(ZenPosition position); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolArgument.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolArgument.java new file mode 100644 index 0000000..f7d7bd5 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolArgument.java @@ -0,0 +1,31 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.symbols; + +import stanhebben.zenscript.expression.ExpressionArgument; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class SymbolArgument implements IZenSymbol { + private final int id; + private final ZenType type; + + public SymbolArgument(int id, ZenType type) { + this.id = id; + this.type = type; + } + + @Override + public IPartialExpression instance(ZenPosition position) { + return new ExpressionArgument(position, id, type); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolJavaClass.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolJavaClass.java new file mode 100644 index 0000000..516210b --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolJavaClass.java @@ -0,0 +1,29 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.symbols; + +import java.lang.reflect.Type; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.expression.partial.PartialJavaClass; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class SymbolJavaClass implements IZenSymbol { + private final Type cls; + + public SymbolJavaClass(Type cls) { + this.cls = cls; + } + + @Override + public IPartialExpression instance(ZenPosition position) { + return new PartialJavaClass(position, cls); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolJavaStaticField.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolJavaStaticField.java new file mode 100644 index 0000000..14f2a5e --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolJavaStaticField.java @@ -0,0 +1,34 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.symbols; + +import java.lang.reflect.Field; +import stanhebben.zenscript.compiler.ITypeRegistry; +import stanhebben.zenscript.expression.partial.ExpressionJavaStaticField; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +public class SymbolJavaStaticField implements IZenSymbol { + private final Class cls; + private final Field field; + private final ITypeRegistry types; + + public SymbolJavaStaticField(Class cls, Field field, ITypeRegistry types) { + this.cls = cls; + this.field = field; + this.types = types; + } + + @Override + public IPartialExpression instance(ZenPosition position) { + return new ExpressionJavaStaticField(position, cls, field, types); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolJavaStaticGetter.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolJavaStaticGetter.java new file mode 100644 index 0000000..a16df7f --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolJavaStaticGetter.java @@ -0,0 +1,29 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.symbols; + +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.expression.partial.PartialStaticGetter; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class SymbolJavaStaticGetter implements IZenSymbol { + private final JavaMethod method; + + public SymbolJavaStaticGetter(JavaMethod method) { + this.method = method; + } + + @Override + public IPartialExpression instance(ZenPosition position) { + return new PartialStaticGetter(position, method); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolJavaStaticMethod.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolJavaStaticMethod.java new file mode 100644 index 0000000..d45a656 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolJavaStaticMethod.java @@ -0,0 +1,29 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.symbols; + +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.expression.partial.PartialStaticMethod; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class SymbolJavaStaticMethod implements IZenSymbol { + private final JavaMethod method; + + public SymbolJavaStaticMethod(JavaMethod method) { + this.method = method; + } + + @Override + public IPartialExpression instance(ZenPosition position) { + return new PartialStaticMethod(position, method); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolLocal.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolLocal.java new file mode 100644 index 0000000..39d37e3 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolLocal.java @@ -0,0 +1,39 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.symbols; + +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.expression.partial.PartialLocal; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class SymbolLocal implements IZenSymbol { + private final ZenType type; + private final boolean isFinal; + + public SymbolLocal(ZenType type, boolean isFinal) { + this.type = type; + this.isFinal = isFinal; + } + + public ZenType getType() { + return type; + } + + public boolean isFinal() { + return isFinal; + } + + @Override + public IPartialExpression instance(ZenPosition position) { + return new PartialLocal(position, this); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolPackage.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolPackage.java new file mode 100644 index 0000000..4baf674 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolPackage.java @@ -0,0 +1,68 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.symbols; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import stanhebben.zenscript.IZenErrorLogger; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.expression.partial.PartialPackage; +import stanhebben.zenscript.util.StringUtil; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class SymbolPackage implements IZenSymbol { + private final HashMap members; + + public SymbolPackage() { + members = new HashMap(); + } + + public Map getPackages() { + return members; + } + + public IZenSymbol get(String name) { + return members.get(name); + } + + public void put(String name, IZenSymbol symbol, IZenErrorLogger errors) { + String[] parts = StringUtil.split(name, '.'); + String[] pkgParts = Arrays.copyOf(parts, parts.length - 1); + SymbolPackage pkgCurrent = this; + for (String part : pkgParts) { + if (pkgCurrent.members.containsKey(part)) { + IZenSymbol member = pkgCurrent.members.get(part); + if (member instanceof SymbolPackage) { + pkgCurrent = (SymbolPackage) member; + } else { + errors.error(null, part + " is not a package"); + return; + } + } else { + SymbolPackage child = new SymbolPackage(); + pkgCurrent.members.put(part, child); + pkgCurrent = child; + } + } + + if (pkgCurrent.members.containsKey(parts[parts.length - 1])) { + errors.error(null, parts[parts.length - 1] + " is already defined in that package"); + } else { + pkgCurrent.members.put(parts[parts.length - 1], symbol); + } + } + + @Override + public IPartialExpression instance(ZenPosition position) { + return new PartialPackage(position, this); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolZenStaticMethod.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolZenStaticMethod.java new file mode 100644 index 0000000..2ebf058 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/symbols/SymbolZenStaticMethod.java @@ -0,0 +1,37 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.symbols; + +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.expression.partial.PartialStaticGenerated; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +public class SymbolZenStaticMethod implements IZenSymbol { + private final String className; + private final String methodName; + private final String signature; + private final ZenType[] argumentTypes; + private final ZenType returnType; + + public SymbolZenStaticMethod(String className, String methodName, String signature, ZenType[] argumentTypes, ZenType returnType) { + this.className = className; + this.methodName = methodName; + this.signature = signature; + this.argumentTypes = argumentTypes; + this.returnType = returnType; + } + + @Override + public IPartialExpression instance(ZenPosition position) { + return new PartialStaticGenerated(position, className, methodName, signature, argumentTypes, returnType); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/ZenNamespaceLocal.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/ZenNamespaceLocal.java new file mode 100644 index 0000000..aece804 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/symbols/ZenNamespaceLocal.java @@ -0,0 +1,45 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.symbols; + +import stanhebben.zenscript.expression.partial.IPartialExpression; +import java.util.HashMap; +import java.util.Map; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ZenNamespaceLocal implements IZenNamespace { + private final IZenNamespace parent; + private final Map values; + + public ZenNamespaceLocal(IZenNamespace parent) { + this.parent = parent; + this.values = new HashMap(); + } + + @Override + public IZenCompileEnvironment getEnvironment() { + return parent.getEnvironment(); + } + + @Override + public IPartialExpression getValue(String name, ZenPosition position) { + if (values.containsKey(name)) { + return values.get(name).instance(position); + } else { + return parent.getValue(name, position); + } + } + + @Override + public void putValue(String name, IZenSymbol value) { + values.put(name, value); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/symbols/ZenNamespaceScript.java b/ZenScript/src/main/java/stanhebben/zenscript/symbols/ZenNamespaceScript.java new file mode 100644 index 0000000..41d94ff --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/symbols/ZenNamespaceScript.java @@ -0,0 +1,50 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.symbols; + +import stanhebben.zenscript.expression.partial.IPartialExpression; +import java.util.HashMap; +import java.util.Map; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ZenNamespaceScript implements IZenNamespace { + private final IZenCompileEnvironment environment; + private final Map values; + + public ZenNamespaceScript(IZenCompileEnvironment environment) { + this.environment = environment; + this.values = new HashMap(); + } + + @Override + public IZenCompileEnvironment getEnvironment() { + return environment; + } + + @Override + public IPartialExpression getValue(String name, ZenPosition position) { + if (values.containsKey(name)) { + return values.get(name).instance(position); + } else { + IZenSymbol symbol = environment.getGlobal(name); + if (symbol == null) { + return null; + } else { + return symbol.instance(position); + } + } + } + + @Override + public void putValue(String name, IZenSymbol value) { + values.put(name, value); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/IZenIterator.java b/ZenScript/src/main/java/stanhebben/zenscript/type/IZenIterator.java new file mode 100644 index 0000000..ab78fbd --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/IZenIterator.java @@ -0,0 +1,48 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type; + +import org.objectweb.asm.Label; + +/** + * + * @author Stanneke + */ +public interface IZenIterator { + /** + * Compiles the header before the iteration. The list is on the top of the stack. + * + * @param locals + */ + public void compileStart(int[] locals); + + /** + * Compiles the start of an iteration. The stack is unmodified from the + * previous iteration and from the iteration start. + * + * @param locals + * @param exit + */ + public void compilePreIterate(int[] locals, Label exit); + + /** + * Compiles the end of an iteration. The stack is the same as it was after + * preIterate. + * + * @param locals + * @param exit + * @param repeat + */ + public void compilePostIterate(int[] locals, Label exit, Label repeat); + + /** + * Compiles the end of the whole iteration. + */ + public void compileEnd(); + + public ZenType getType(int i); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenType.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenType.java new file mode 100644 index 0000000..1546cb5 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenType.java @@ -0,0 +1,277 @@ +package stanhebben.zenscript.type; + +import java.io.IOException; +import org.objectweb.asm.Type; +import stanhebben.zenscript.TypeExpansion; +import stanhebben.zenscript.ZenParser; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.parser.ParseException; +import stanhebben.zenscript.parser.Token; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.type.expand.ZenExpandCaster; +import stanhebben.zenscript.util.ZenPosition; + +public abstract class ZenType { + public static final ZenTypeAny ANY = ZenTypeAny.INSTANCE; + public static final ZenTypeBool BOOL = ZenTypeBool.INSTANCE; + public static final ZenTypeByte BYTE = ZenTypeByte.INSTANCE; + public static final ZenTypeShort SHORT = ZenTypeShort.INSTANCE; + public static final ZenTypeInt INT = ZenTypeInt.INSTANCE; + public static final ZenTypeLong LONG = ZenTypeLong.INSTANCE; + public static final ZenTypeFloat FLOAT = ZenTypeFloat.INSTANCE; + public static final ZenTypeDouble DOUBLE = ZenTypeDouble.INSTANCE; + public static final ZenTypeString STRING = ZenTypeString.INSTANCE; + public static final ZenTypeVoid VOID = ZenTypeVoid.INSTANCE; + public static final ZenTypeNull NULL = ZenTypeNull.INSTANCE; + + public static final int NUM_BYTE = 1; + public static final int NUM_SHORT = 2; + public static final int NUM_INT = 3; + public static final int NUM_LONG = 4; + public static final int NUM_FLOAT = 5; + public static final int NUM_DOUBLE = 6; + + public static ZenType parse(String type, IEnvironmentGlobal environment) { + try { + ZenParser parser = new ZenParser(type, environment.getEnvironment()); + return read(parser, environment); + } catch (IOException ex) { + return null; + } + } + + public static ZenType read(ZenParser parser, IEnvironmentGlobal environment) { + ZenType base; + + Token next = parser.next(); + switch (next.getType()) { + case ZenParser.T_ANY: + base = ANY; + break; + case ZenParser.T_VOID: + base = VOID; + break; + case ZenParser.T_BOOL: + base = BOOL; + break; + case ZenParser.T_BYTE: + base = BYTE; + break; + case ZenParser.T_SHORT: + base = SHORT; + break; + case ZenParser.T_INT: + base = INT; + break; + case ZenParser.T_LONG: + base = LONG; + break; + case ZenParser.T_FLOAT: + base = FLOAT; + break; + case ZenParser.T_DOUBLE: + base = DOUBLE; + break; + case ZenParser.T_STRING: + base = STRING; + break; + case ZenParser.T_ID: + IPartialExpression partial = environment.getValue(next.getValue(), next.getPosition()); + while (parser.optional(ZenParser.T_DOT) != null) { + Token member = parser.required(ZenParser.T_ID, "identifier expected"); + partial = partial.getMember(member.getPosition(), environment, member.getValue()); + } + base = partial.toType(environment); + break; + default: + throw new ParseException(next, "Unknown type: " + next.getValue()); + } + + while (parser.optional(ZenParser.T_SQBROPEN) != null) { + if (parser.optional(ZenParser.T_SQBRCLOSE) == null) { + base = new ZenTypeAssociative(base, read(parser, environment)); + parser.required(ZenParser.T_SQBRCLOSE, "] expected"); + } else { + base = new ZenTypeArrayBasic(base); + } + } + + return base; + } + + public abstract Expression unary( + ZenPosition position, + IEnvironmentGlobal environment, + Expression value, + OperatorType operator); + + public abstract Expression binary( + ZenPosition position, + IEnvironmentGlobal environment, + Expression left, + Expression right, + OperatorType operator); + + public abstract Expression trinary( + ZenPosition position, + IEnvironmentGlobal environment, + Expression first, + Expression second, + Expression third, + OperatorType operator); + + public abstract Expression compare( + ZenPosition position, + IEnvironmentGlobal environment, + Expression left, + Expression right, + CompareType type); + + public abstract IPartialExpression getMember( + ZenPosition position, + IEnvironmentGlobal environment, + IPartialExpression value, + String name); + + public abstract IPartialExpression getStaticMember( + ZenPosition position, + IEnvironmentGlobal environment, + String name); + + public abstract Expression call( + ZenPosition position, + IEnvironmentGlobal environment, + Expression receiver, + Expression... arguments); + + public abstract Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type); + + public abstract IZenIterator makeIterator( + int numValues, + IEnvironmentMethod methodOutput); + + public abstract boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment); + + public abstract boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment); + + public abstract Class toJavaClass(); + + public abstract Type toASMType(); + + public abstract int getNumberType(); + + public abstract String getSignature(); + + public abstract boolean isPointer(); + + public abstract void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type); + + public abstract String getName(); + + public abstract Expression defaultValue(ZenPosition position); + + public boolean isLarge() { + return false; + } + + @Override + public String toString() { + return getName(); + } + + protected Expression unaryExpansion(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + TypeExpansion expansion = environment.getExpansion(getName()); + if (expansion != null) { + return expansion.unary(position, environment, value, operator); + } + return null; + } + + protected Expression binaryExpansion( + ZenPosition position, + IEnvironmentGlobal environment, + Expression left, + Expression right, + OperatorType operator) { + TypeExpansion expansion = environment.getExpansion(getName()); + if (expansion != null) { + return expansion.binary(position, environment, left, right, operator); + } + return null; + } + + protected Expression trinaryExpansion( + ZenPosition position, + IEnvironmentGlobal environment, + Expression first, + Expression second, + Expression third, + OperatorType operator) { + TypeExpansion expansion = environment.getExpansion(getName()); + if (expansion != null) { + return expansion.trinary(position, environment, first, second, third, operator); + } + return null; + } + + protected IPartialExpression memberExpansion(ZenPosition position, IEnvironmentGlobal environment, Expression value, String member) { + TypeExpansion expansion = environment.getExpansion(getName()); + if (expansion != null) { + return expansion.member(position, environment, value, member); + } + return null; + } + + protected IPartialExpression staticMemberExpansion(ZenPosition position, IEnvironmentGlobal environment, String member) { + TypeExpansion expansion = environment.getExpansion(getName()); + if (expansion != null) { + return expansion.staticMember(position, environment, member); + } + return null; + } + + protected Expression castExpansion( + ZenPosition position, + IEnvironmentGlobal environment, + Expression value, + ZenType toType) { + TypeExpansion expansion = environment.getExpansion(getName()); + if (expansion != null) { + ZenExpandCaster caster = expansion.getCaster(toType, environment); + if (caster != null) { + return caster.cast(position, environment, value); + } + } + return null; + } + + protected boolean canCastExpansion(IEnvironmentGlobal environment, ZenType toType) { + String name = getName(); + TypeExpansion expansion = environment.getExpansion(name); + if (expansion != null) { + ZenExpandCaster caster = expansion.getCaster(toType, environment); + if (caster != null) { + return true; + } + } + + return false; + } + + protected boolean compileCastExpansion(ZenPosition position, IEnvironmentMethod environment, ZenType toType) { + TypeExpansion expansion = environment.getExpansion(getName()); + if (expansion != null) { + ZenExpandCaster caster = expansion.getCaster(toType, environment); + if (caster != null) { + caster.compile(environment); + return true; + } + } + + return false; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeAny.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeAny.java new file mode 100644 index 0000000..4af896a --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeAny.java @@ -0,0 +1,409 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type; + +import java.util.HashMap; +import java.util.Map; +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionAs; +import stanhebben.zenscript.expression.ExpressionCompareGeneric; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.ExpressionNull; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.MethodCompiler; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.signature; +import stanhebben.zenscript.value.IAny; + +/** + * + * @author Stanneke + */ +public class ZenTypeAny extends ZenType { + public static final ZenTypeAny INSTANCE = new ZenTypeAny(); + + private static final Map CASTERS; + + static { + CASTERS = new HashMap(); + CASTERS.put("bool", new MethodCompiler() { + @Override + public void compile(MethodOutput output) { + output.invokeInterface(IAny.class, "asBool", boolean.class); + } + }); + CASTERS.put("byte", new MethodCompiler() { + @Override + public void compile(MethodOutput output) { + output.invokeInterface(IAny.class, "asByte", byte.class); + } + }); + CASTERS.put("short", new MethodCompiler() { + @Override + public void compile(MethodOutput output) { + output.invokeInterface(IAny.class, "asShort", short.class); + } + }); + CASTERS.put("int", new MethodCompiler() { + @Override + public void compile(MethodOutput output) { + output.invokeInterface(IAny.class, "asInt", int.class); + } + }); + CASTERS.put("long", new MethodCompiler() { + @Override + public void compile(MethodOutput output) { + output.invokeInterface(IAny.class, "asLong", long.class); + } + }); + CASTERS.put("float", new MethodCompiler() { + @Override + public void compile(MethodOutput output) { + output.invokeInterface(IAny.class, "asFloat", float.class); + } + }); + CASTERS.put("double", new MethodCompiler() { + @Override + public void compile(MethodOutput output) { + output.invokeInterface(IAny.class, "asDouble", double.class); + } + }); + CASTERS.put(Boolean.class.getName(), new MethodCompiler() { + @Override + public void compile(MethodOutput output) { + output.invokeInterface(IAny.class, "asBool", boolean.class); + output.invokeStatic(Boolean.class, "valueOf", Boolean.class, boolean.class); + } + }); + CASTERS.put(Byte.class.getName(), new MethodCompiler() { + @Override + public void compile(MethodOutput output) { + output.invokeInterface(IAny.class, "asByte", boolean.class); + output.invokeStatic(Byte.class, "valueOf", Byte.class, byte.class); + } + }); + CASTERS.put(Short.class.getName(), new MethodCompiler() { + @Override + public void compile(MethodOutput output) { + output.invokeInterface(IAny.class, "asShort", short.class); + output.invokeStatic(Short.class, "valueOf", Short.class, short.class); + } + }); + CASTERS.put(Integer.class.getName(), new MethodCompiler() { + @Override + public void compile(MethodOutput output) { + output.invokeInterface(IAny.class, "asInt", int.class); + output.invokeStatic(Integer.class, "valueOf", Integer.class, int.class); + } + }); + CASTERS.put(Long.class.getName(), new MethodCompiler() { + @Override + public void compile(MethodOutput output) { + output.invokeInterface(IAny.class, "asLong", long.class); + output.invokeStatic(Long.class, "valueOf", Long.class, long.class); + } + }); + CASTERS.put(Float.class.getName(), new MethodCompiler() { + @Override + public void compile(MethodOutput output) { + output.invokeInterface(IAny.class, "asFloat", float.class); + output.invokeStatic(Float.class, "valueOf", Float.class, float.class); + } + }); + CASTERS.put(Double.class.getName(), new MethodCompiler() { + @Override + public void compile(MethodOutput output) { + output.invokeInterface(IAny.class, "asDouble", double.class); + output.invokeStatic(Double.class, "valueOf", Double.class, double.class); + } + }); + CASTERS.put(String.class.getName(), new MethodCompiler() { + @Override + public void compile(MethodOutput output) { + output.invokeInterface(IAny.class, "asString", String.class); + } + }); + } + + private ZenTypeAny() {} + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + return null; + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return null; + } + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + return null; // TODO: how to handle iteration on any-values + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return true; // true or not? + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return true; + } + + @Override + public Class toJavaClass() { + return IAny.class; + } + + @Override + public Type toASMType() { + return Type.getType(IAny.class); + } + + @Override + public int getNumberType() { + return 0; + } + + @Override + public String getSignature() { + return signature(IAny.class); + } + + @Override + public boolean isPointer() { + return true; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType toType) { + if (CASTERS.containsKey(toType.getName())) { + CASTERS.get(toType.getName()).compile(environment.getOutput()); + } else { + if (!compileCastExpansion(position, environment, toType)) { + environment.getOutput().constant(toType); + environment.getOutput().invokeInterface(IAny.class, "as", Class.class, Object.class); + } + } + } + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + //try { + switch (operator) { + /*case NEG: + return new ExpressionJavaCallVirtual( + position, + IAny.class, + IAny.class.getMethod("neg"), + ZenTypeAny.INSTANCE, + value); + case NOT: + return new ExpressionJavaCallVirtual( + position, + IAny.class, + IAny.class.getMethod("not"), + ZenTypeAny.INSTANCE, + value);*/ + default: + return new ExpressionInvalid(position, ZenTypeAny.INSTANCE); + } + /*} catch (NoSuchMethodException ex) { + throw new RuntimeException("could not find method", ex); + }*/ + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + //try { + switch (operator) { + /*case ADD: + return new ExpressionJavaCallVirtual( + position, + IAny.class, + IAny.class.getMethod("add", IAny.class), + ZenTypeAny.INSTANCE, + left, + right.cast(position, environment, ANY)); + case CAT: + return new ExpressionJavaCallVirtual( + position, + IAny.class, + IAny.class.getMethod("cat", IAny.class), + ZenTypeAny.INSTANCE, + left, + right.cast(position, environment, ANY)); + case SUB: + return new ExpressionJavaCallVirtual( + position, + IAny.class, + IAny.class.getMethod("sub", IAny.class), + ZenTypeAny.INSTANCE, + left, + right.cast(position, environment, ANY)); + case MUL: + return new ExpressionJavaCallVirtual( + position, + IAny.class, + IAny.class.getMethod("mul", IAny.class), + ZenTypeAny.INSTANCE, + left, + right.cast(position, environment, ANY)); + case DIV: + return new ExpressionJavaCallVirtual( + position, + IAny.class, + IAny.class.getMethod("div", IAny.class), + ZenTypeAny.INSTANCE, + left, + right.cast(position, environment, ANY)); + case MOD: + return new ExpressionJavaCallVirtual( + position, + IAny.class, + IAny.class.getMethod("mod", IAny.class), + ZenTypeAny.INSTANCE, + left, + right.cast(position, environment, ANY)); + case AND: + return new ExpressionJavaCallVirtual( + position, + IAny.class, + IAny.class.getMethod("and", IAny.class), + ZenTypeAny.INSTANCE, + left, + right.cast(position, environment, ANY)); + case OR: + return new ExpressionJavaCallVirtual( + position, + IAny.class, + IAny.class.getMethod("or", IAny.class), + ZenTypeAny.INSTANCE, + left, + right.cast(position, environment, ANY)); + case XOR: + return new ExpressionJavaCallVirtual( + position, + IAny.class, + IAny.class.getMethod("xor", IAny.class), + ZenTypeAny.INSTANCE, + left, + right.cast(position, environment, ANY)); + case CONTAINS: + return new ExpressionJavaCallVirtual( + position, + IAny.class, + IAny.class.getMethod("contains", IAny.class), + ZenTypeAny.INSTANCE, + left, + right.cast(position, environment, ANY)); + case INDEXGET: + return new ExpressionJavaCallVirtual( + position, + IAny.class, + IAny.class.getMethod("indexGet", IAny.class), + ZenTypeAny.INSTANCE, + left, + right.cast(position, environment, ANY)); + case RANGE: + return new ExpressionJavaCallVirtual( + position, + IAny.class, + IAny.class.getMethod("range", IAny.class), + ZenTypeAny.INSTANCE, + left, + right.cast(position, environment, ANY)); + case COMPARE: + return new ExpressionJavaCallVirtual( + position, + IAny.class, + IAny.class.getMethod("compareTo", IAny.class), + ZenTypeAny.INSTANCE, + left, + right.cast(position, environment, ANY));*/ + default: + return new ExpressionInvalid(position, ANY); + } + /*} catch (NoSuchMethodException ex) { + throw new RuntimeException("method not found", ex); + }*/ + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + //try { + switch (operator) { + /*case INDEXSET: + return new ExpressionJavaCallVirtual( + position, + IAny.class, + IAny.class.getMethod("indexSet", IAny.class, IAny.class), + ZenTypeAny.INSTANCE, + first, + second.cast(position, environment, ANY), + third.cast(position, environment, ANY));*/ + default: + return new ExpressionInvalid(position, ANY); + } + /*} catch (NoSuchMethodException ex) { + throw new RuntimeException("method not found", ex); + }*/ + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + Expression comparator = JavaMethod + .get(environment, IAny.class, "compareTo", IAny.class) + .callVirtual(position, environment, left, right); + + return new ExpressionCompareGeneric( + position, + comparator, + type); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + try { + return JavaMethod + .get(environment, IAny.class, "call", Class.forName("[" + signature(IAny.class))) + .callVirtual(position, environment, receiver, arguments); + } catch (ClassNotFoundException ex) { + throw new RuntimeException(ex); + } + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type.equals(this)) return value; + + if (canCastExpansion(environment, type)) { + return castExpansion(position, environment, value, type); + } else { + return new ExpressionAs(position, value, type); + } + } + + @Override + public String getName() { + return "any"; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionNull(position); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeArray.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeArray.java new file mode 100644 index 0000000..978b81a --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeArray.java @@ -0,0 +1,161 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type; + +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionCompareGeneric; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.ExpressionNull; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +public abstract class ZenTypeArray extends ZenType { + private final ZenType base; + private final String name; + + public ZenTypeArray(ZenType base) { + this.base = base; + name = base + "[]"; + } + + public ZenType getBaseType() { + return base; + } + + public abstract IPartialExpression getMemberLength(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value); + + public abstract Expression indexGet(ZenPosition position, IEnvironmentGlobal environment, Expression array, Expression index); + + public abstract Expression indexSet(ZenPosition position, IEnvironmentGlobal environment, Expression array, Expression index, Expression value); + + @Override + public final String getName() { + return name; + } + + @Override + public final boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return equals(type) || canCastExpansion(environment, type); + } + + @Override + public final boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return equals(type) || canCastExpansion(environment, type); + } + + @Override + public final int getNumberType() { + return 0; + } + + @Override + public final IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + if (name.equals("length")) { + return getMemberLength(position, environment, value); + } else { + IPartialExpression result = memberExpansion(position, environment, value.eval(environment), name); + if (result == null) { + environment.error(position, "no such member in array: " + name); + return new ExpressionInvalid(position); + } else { + return result; + } + } + } + + @Override + public final IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + IPartialExpression result = staticMemberExpansion(position, environment, name); + if (result == null) { + environment.error(position, "no such member in array: " + name); + return new ExpressionInvalid(position); + } else { + return result; + } + } + + @Override + public final boolean isPointer() { + return true; + } + + @Override + public final Expression unary( + ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + Expression result = unaryExpansion(position, environment, value, operator); + if (result == null) { + environment.error(position, "Array has no unary operators"); + return new ExpressionInvalid(position); + } else { + return result; + } + } + + @Override + public final Expression binary( + ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + if (operator == OperatorType.INDEXGET) { + return indexGet(position, environment, left, right); + } else { + Expression result = binaryExpansion(position, environment, left, right, operator); + if (result == null) { + environment.error(position, getName() + " doesn't have such operator"); + return new ExpressionInvalid(position); + } else { + return result; + } + } + } + + @Override + public final Expression trinary( + ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + if (operator == OperatorType.INDEXSET) { + return indexSet(position, environment, first, second, third); + } else { + Expression result = trinaryExpansion(position, environment, first, second, third, operator); + if (result == null) { + environment.error(position, getName() + " doesn't have such operator"); + return new ExpressionInvalid(position); + } else { + return result; + } + } + } + + @Override + public final Expression compare( + ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + Expression compare = binaryExpansion(position, environment, left, right, OperatorType.COMPARE); + if (compare == null) { + environment.error(position, "Arrays cannot be compared"); + return new ExpressionInvalid(position, this); + } else { + return new ExpressionCompareGeneric(position, compare, type); + } + } + + @Override + public final Expression call( + ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + environment.error(position, "Cannot call an array"); + return new ExpressionInvalid(position); + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionNull(position); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeArrayBasic.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeArrayBasic.java new file mode 100644 index 0000000..60bfd76 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeArrayBasic.java @@ -0,0 +1,252 @@ +package stanhebben.zenscript.type; + +import java.util.Arrays; +import java.util.List; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionArrayGet; +import stanhebben.zenscript.expression.ExpressionArrayLength; +import stanhebben.zenscript.expression.ExpressionArraySet; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +public class ZenTypeArrayBasic extends ZenTypeArray { + private final Type asmType; + + public ZenTypeArrayBasic(ZenType base) { + super(base); + + asmType = Type.getType("[" + base.toASMType().getDescriptor()); + } + + @Override + public boolean equals(Object other) { + if (other instanceof ZenTypeArrayBasic) { + ZenTypeArrayBasic o = (ZenTypeArrayBasic) other; + return o.getBaseType().equals(getBaseType()); + } + + return false; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 23 * hash + (this.getBaseType() != null ? this.getBaseType().hashCode() : 0); + return hash; + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (equals(type)) return value; + + return castExpansion(position, environment, value, type); + } + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + if (numValues == 1) { + return new ValueIterator(methodOutput.getOutput()); + } else if (numValues == 2) { + return new IndexValueIterator(methodOutput.getOutput()); + } else { + return null; + } + } + + @Override + public Type toASMType() { + return asmType; + } + + @Override + public Class toJavaClass() { + try { + return Class.forName("[" + getBaseType().toJavaClass().getName()); + } catch (ClassNotFoundException ex) { + throw new RuntimeException(ex); + } + } + + @Override + public String getSignature() { + return "[" + getBaseType().getSignature(); + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType toClass) { + MethodOutput output = environment.getOutput(); + + if (compileCastExpansion(position, environment, toClass)) { + // OK + } else if (toClass instanceof ZenTypeArrayList) { + // convert array to list + // Arrays.asList(value); + output.invokeStatic(Arrays.class, "asList", List.class, Object[].class); + } else if (toClass instanceof ZenTypeArrayBasic) { + // convert elements + if (toClass.equals(this)) { + // do nothing + } else { + ZenType component = ((ZenTypeArrayBasic) toClass).getBaseType(); + Type componentType = component.toASMType(); + + int result = output.local(componentType); + + output.dup(); + output.arrayLength(); + output.newArray(componentType); + output.storeObject(result); + + output.iConst0(); + + Label lbl = new Label(); + output.label(lbl); + + // stack: original index + output.dupX1(); + output.dupX1(); + output.arrayLoad(componentType); + + // stack: original index value + getBaseType().compileCast(position, environment, toClass); + + output.loadObject(result); + output.dupX2(); + output.dupX2(); + output.arrayStore(componentType); + output.pop(); + + // stack: original index + output.iConst1(); + output.iAdd(); + output.dupX1(); + output.arrayLength(); + output.ifICmpGE(lbl); + + output.pop(); + output.pop(); + + output.loadObject(result); + } + } else { + // TODO: error + } + } + + @Override + public IPartialExpression getMemberLength(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value) { + return new ExpressionArrayLength(position, value.eval(environment)); + } + + @Override + public Expression indexGet(ZenPosition position, IEnvironmentGlobal environment, Expression array, Expression index) { + return new ExpressionArrayGet( + position, + array, + index.cast(position, environment, INT)); + } + + @Override + public Expression indexSet(ZenPosition position, IEnvironmentGlobal environment, Expression array, Expression index, Expression value) { + return new ExpressionArraySet( + position, + array, + index.cast(position, environment, INT), + value.cast(position, environment, getBaseType())); + } + + private class ValueIterator implements IZenIterator { + private final MethodOutput methodOutput; + private int index; + + public ValueIterator(MethodOutput methodOutput) { + this.methodOutput = methodOutput; + } + + @Override + public void compileStart(int[] locals) { + index = methodOutput.local(Type.INT_TYPE); + methodOutput.iConst0(); + methodOutput.storeInt(index); + } + + @Override + public void compilePreIterate(int[] locals, Label exit) { + methodOutput.dup(); + methodOutput.arrayLength(); + methodOutput.loadInt(index); + methodOutput.ifICmpGE(exit); + + methodOutput.dup(); + methodOutput.loadInt(index); + methodOutput.arrayLoad(getBaseType().toASMType()); + methodOutput.store(getBaseType().toASMType(), locals[0]); + } + + @Override + public void compilePostIterate(int[] locals, Label exit, Label repeat) { + methodOutput.iinc(index, 1); + methodOutput.goTo(repeat); + } + + @Override + public void compileEnd() { + // pop the array + methodOutput.pop(); + } + + @Override + public ZenType getType(int i) { + return getBaseType(); + } + } + + private class IndexValueIterator implements IZenIterator { + private final MethodOutput methodOutput; + + public IndexValueIterator(MethodOutput methodOutput) { + this.methodOutput = methodOutput; + } + + @Override + public void compileStart(int[] locals) { + methodOutput.iConst0(); + methodOutput.storeInt(locals[0]); + } + + @Override + public void compilePreIterate(int[] locals, Label exit) { + methodOutput.dup(); + methodOutput.arrayLength(); + methodOutput.loadInt(locals[0]); + methodOutput.ifICmpGE(exit); + + methodOutput.dup(); + methodOutput.loadInt(locals[0]); + methodOutput.arrayLoad(getBaseType().toASMType()); + methodOutput.store(getBaseType().toASMType(), locals[1]); + } + + @Override + public void compilePostIterate(int[] locals, Label exit, Label repeat) { + methodOutput.iinc(locals[0]); + methodOutput.goTo(repeat); + } + + @Override + public void compileEnd() { + // pop the array + methodOutput.pop(); + } + + @Override + public ZenType getType(int i) { + return getBaseType(); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeArrayList.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeArrayList.java new file mode 100644 index 0000000..b13647f --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeArrayList.java @@ -0,0 +1,166 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type; + +import java.util.List; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.type.iterator.IteratorIterable; +import stanhebben.zenscript.type.iterator.IteratorList; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.signature; + +/** + * + * @author Stan + */ +public class ZenTypeArrayList extends ZenTypeArray { + private final Type type; + + public ZenTypeArrayList(ZenType baseType) { + super(baseType); + + type = Type.getType(List.class); + } + + @Override + public IPartialExpression getMemberLength(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value) { + return new ExpressionListLength(position, value.eval(environment)); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (equals(type)) return value; + + return castExpansion(position, environment, value, type); + } + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + if (numValues == 1) { + return new IteratorIterable(methodOutput.getOutput(), getBaseType()); + } else if (numValues == 2) { + return new IteratorList(methodOutput.getOutput(), getBaseType()); + } else { + return null; + } + } + + @Override + public Class toJavaClass() { + return List.class; + } + + @Override + public Type toASMType() { + return Type.getType(List.class); + } + + @Override + public String getSignature() { + return signature(List.class); + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + MethodOutput output = environment.getOutput(); + + if (equals(type)) { + return; + } + + if (compileCastExpansion(position, environment, type)) { + // OK + } else if (type instanceof ZenTypeArrayList) { + // TODO: implement + } else if (type instanceof ZenTypeArrayBasic) { + // convert elements + ZenType component = ((ZenTypeArrayBasic) type).getBaseType(); + Type componentType = component.toASMType(); + Type baseType = getBaseType().toASMType(); + + int result = output.local(componentType); + + output.dup(); + output.arrayLength(); + output.newArray(baseType); + output.storeObject(result); + + output.iConst0(); + + Label lbl = new Label(); + output.label(lbl); + + // stack: original index + output.dupX1(); + output.dupX1(); + output.arrayLoad(componentType); + + // stack: original index value + getBaseType().compileCast(position, environment, type); + + output.loadObject(result); + output.dupX2(); + output.dupX2(); + output.arrayStore(baseType); + output.pop(); + + // stack: original index + output.iConst1(); + output.iAdd(); + output.dupX1(); + output.arrayLength(); + output.ifICmpGE(lbl); + + output.pop(); + output.pop(); + + output.loadObject(result); + } else { + // TODO: error + } + } + + @Override + public Expression indexGet(ZenPosition position, IEnvironmentGlobal environment, Expression array, Expression index) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Expression indexSet(ZenPosition position, IEnvironmentGlobal environment, Expression array, Expression index, Expression value) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + private class ExpressionListLength extends Expression { + private final Expression value; + + public ExpressionListLength(ZenPosition position, Expression value) { + super(position); + + this.value = value; + } + + @Override + public ZenType getType() { + return ZenTypeInt.INSTANCE; + } + + @Override + public void compile(boolean result, IEnvironmentMethod environment) { + value.compile(result, environment); + + if (result) { + environment.getOutput().invokeInterface(List.class, "size", int.class); + } + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeAssociative.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeAssociative.java new file mode 100644 index 0000000..13ab092 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeAssociative.java @@ -0,0 +1,233 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type; + +import java.util.Map; +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionCompareGeneric; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.ExpressionMapContains; +import stanhebben.zenscript.expression.ExpressionMapIndexGet; +import stanhebben.zenscript.expression.ExpressionMapIndexSet; +import stanhebben.zenscript.expression.ExpressionMapSize; +import stanhebben.zenscript.expression.ExpressionNull; +import stanhebben.zenscript.expression.ExpressionString; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.type.iterator.IteratorMap; +import stanhebben.zenscript.type.iterator.IteratorMapKeys; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.signature; + +/** + * + * @author Stanneke + */ +public class ZenTypeAssociative extends ZenType { + private final ZenType base; + private final ZenType key; + + private final String name; + + public ZenTypeAssociative(ZenType base, ZenType key) { + this.base = base; + this.key = key; + + name = base.getName() + "[" + key.getName() + "]"; + } + + public ZenType getValueType() { + return base; + } + + public ZenType getKeyType() { + return key; + } + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + Expression result = unaryExpansion(position, environment, value, operator); + if (result == null) { + environment.error(position, "associative arrays don't have unary operators"); + return new ExpressionInvalid(position); + } else { + return result; + } + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + if (operator == OperatorType.CONTAINS) { + return new ExpressionMapContains(position, left, right.cast(position, environment, key)); + } else if (operator == OperatorType.INDEXGET) { + return new ExpressionMapIndexGet(position, left, right.cast(position, environment, key)); + } else { + Expression result = binaryExpansion(position, environment, left, right, operator); + if (result == null) { + environment.error(position, "associative arrays don't support this operation"); + return new ExpressionInvalid(position); + } else { + return result; + } + } + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + if (operator == OperatorType.INDEXSET) { + return new ExpressionMapIndexSet( + position, + first, + second.cast(position, environment, key), + third.cast(position, environment, base)); + } else { + Expression result = trinaryExpansion(position, environment, first, second, third, operator); + if (result == null) { + environment.error(position, "associative arrays don't support this operation"); + return new ExpressionInvalid(position); + } else { + return result; + } + } + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + Expression result = binaryExpansion(position, environment, left, right, OperatorType.COMPARE); + if (result == null) { + environment.error(position, "cannot compare associative arrays"); + return new ExpressionInvalid(position); + } else { + return new ExpressionCompareGeneric(position, result, type); + } + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + if (name.equals("length")) { + return new ExpressionMapSize(position, value.eval(environment)); + } else if (ZenTypeString.INSTANCE.canCastImplicit(key, environment)) { + return new ExpressionMapIndexGet( + position, + value.eval(environment), + new ExpressionString(position, name).cast(position, environment, key)); + } else { + IPartialExpression result = memberExpansion(position, environment, value.eval(environment), name); + if (result == null) { + environment.error(position, "this array is not indexable with strings"); + return new ExpressionInvalid(position, base); + } else { + return result; + } + } + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + IPartialExpression result = staticMemberExpansion(position, environment, name); + if (result == null) { + environment.error(position, "associative arrays don't have static members"); + return new ExpressionInvalid(position); + } else { + return result; + } + } + + @Override + public Expression call(ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + environment.error(position, "cannot call associative arrays"); + return new ExpressionInvalid(position); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type == this) { + return value; + } + + return castExpansion(position, environment, value, type); + } + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + if (numValues == 1) { + return new IteratorMapKeys(methodOutput.getOutput(), this); + } else if (numValues == 2) { + return new IteratorMap(methodOutput.getOutput(), this); + } else { + return null; + } + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return type == this || canCastAssociative(type, environment) || canCastExpansion(environment, type); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return type == this || canCastAssociative(type, environment) || canCastExpansion(environment, type); + } + + @Override + public Class toJavaClass() { + return Map.class; + } + + @Override + public Type toASMType() { + return Type.getType(Map.class); + } + + @Override + public int getNumberType() { + return 0; + } + + @Override + public String getSignature() { + return signature(Map.class); + } + + @Override + public boolean isPointer() { + return true; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + if (equals(type)) { + // nothing to do + } else if (!compileCastExpansion(position, environment, type)) { + environment.error(position, "cannot cast " + this + " to " + type); + } + } + + @Override + public String getName() { + return name; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionNull(position); + } + + private boolean canCastAssociative(ZenType type, IEnvironmentGlobal global) { + if (!(type instanceof ZenTypeAssociative)) { + return false; + } + + ZenTypeAssociative atype = (ZenTypeAssociative) type; + return getKeyType().canCastImplicit(atype.getKeyType(), global) + && getValueType().canCastImplicit(atype.getValueType(), global); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeBool.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeBool.java new file mode 100644 index 0000000..8fadb29 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeBool.java @@ -0,0 +1,166 @@ +package stanhebben.zenscript.type; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionArithmeticBinary; +import stanhebben.zenscript.expression.ExpressionArithmeticCompare; +import stanhebben.zenscript.expression.ExpressionArithmeticUnary; +import stanhebben.zenscript.expression.ExpressionAs; +import stanhebben.zenscript.expression.ExpressionBool; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import static stanhebben.zenscript.type.ZenType.STRING; +import stanhebben.zenscript.util.ZenPosition; + +public class ZenTypeBool extends ZenType { + public static final ZenTypeBool INSTANCE = new ZenTypeBool(); + + private ZenTypeBool() {} + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + return null; + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return type == INSTANCE || type == ZenTypeString.INSTANCE || canCastExpansion(environment, type); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return type == INSTANCE || type == ZenTypeString.INSTANCE || canCastExpansion(environment, type); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type == BOOL || type == ZenTypeBoolObject.INSTANCE || type == STRING) { + return new ExpressionAs(position, value, type); + } else if (canCastExpansion(environment, type)) { + return castExpansion(position, environment, value, type); + } else { + return new ExpressionAs(position, value, type); + } + } + + @Override + public Class toJavaClass() { + return boolean.class; + } + + @Override + public Type toASMType() { + return Type.BOOLEAN_TYPE; + } + + @Override + public int getNumberType() { + return 0; + } + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + return new ExpressionArithmeticUnary(position, operator, value); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + if (right.getType().canCastImplicit(ZenTypeBool.INSTANCE, environment)) { + switch (operator) { + case AND: + case OR: + case XOR: + if (right.getType() != ZenTypeBool.INSTANCE) { + right = right.cast(position, environment, ZenTypeBool.INSTANCE); + } + + return new ExpressionArithmeticBinary(position, operator, left, right); + default: + environment.error(position, "unsupported bool operator: " + operator); + return new ExpressionInvalid(position, ZenTypeBool.INSTANCE); + } + } else { + environment.error(right.getPosition(), "not a valid bool value"); + return new ExpressionInvalid(position, ZenTypeBool.INSTANCE); + } + } + + @Override + public Expression trinary( + ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + environment.error(position, "operation not supported on a bool value"); + return new ExpressionInvalid(position, ZenTypeBool.INSTANCE); + } + + @Override + public Expression compare( + ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + if (type == CompareType.EQ || type == CompareType.NE) { + return new ExpressionArithmeticCompare(position, type, left, right); + } else { + environment.error(position, "such comparison not supported on a bool"); + return new ExpressionInvalid(position, ZenTypeBool.INSTANCE); + } + } + + @Override + public Expression call( + ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + environment.error(position, "cannot call a boolean value"); + return new ExpressionInvalid(position, ZenTypeAny.INSTANCE); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + IPartialExpression result = memberExpansion(position, environment, value.eval(environment), name); + if (result == null) { + environment.error(position, "bool value has no members"); + return new ExpressionInvalid(position, ZenTypeAny.INSTANCE); + } else { + return result; + } + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + environment.error(position, "bool type has no static members"); + return new ExpressionInvalid(position, ZenTypeAny.INSTANCE); + } + + @Override + public String getSignature() { + return "Z"; + } + + @Override + public boolean isPointer() { + return false; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + if (type == this) { + // nothing to do + } else if (type == ZenTypeBoolObject.INSTANCE) { + environment.getOutput().invokeStatic(Boolean.class, "valueOf", Boolean.class, boolean.class); + } else if (type == STRING) { + environment.getOutput().invokeStatic(Boolean.TYPE, "toString", String.class, boolean.class); + } else if (!compileCastExpansion(position, environment, type)) { + environment.error(position, "Cannot compile bool to " + type); + } + } + + @Override + public String getName() { + return "bool"; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionBool(position, false); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeBoolObject.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeBoolObject.java new file mode 100644 index 0000000..cf4b1a0 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeBoolObject.java @@ -0,0 +1,141 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionAs; +import stanhebben.zenscript.expression.ExpressionNull; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import static stanhebben.zenscript.type.ZenType.BOOL; +import static stanhebben.zenscript.type.ZenType.STRING; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.signature; + +/** + * + * @author Stan + */ +public class ZenTypeBoolObject extends ZenType { + public static final ZenTypeBoolObject INSTANCE = new ZenTypeBoolObject(); + + private ZenTypeBoolObject() {} + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + return BOOL.unary(position, environment, value.cast(position, environment, ZenTypeBool.INSTANCE), operator); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + return BOOL.binary(position, environment, left.cast(position, environment, BOOL), right, operator); + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + return BOOL.trinary(position, environment, first.cast(position, environment, BOOL), second, third, operator); + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + return BOOL.compare(position, environment, left.cast(position, environment, ZenTypeBool.INSTANCE), right, type); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + return BOOL.getMember(position, environment, value.eval(environment).cast(position, environment, BOOL), name); + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return BOOL.getStaticMember(position, environment, name); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + return BOOL.call(position, environment, receiver.cast(position, environment, BOOL), arguments); + } + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + return BOOL.makeIterator(numValues, methodOutput); + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return BOOL.canCastImplicit(type, environment); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return BOOL.canCastExplicit(type, environment); + } + + @Override + public Class toJavaClass() { + return Boolean.class; + } + + @Override + public Type toASMType() { + return Type.getType(Boolean.class); + } + + @Override + public int getNumberType() { + return 0; + } + + @Override + public String getSignature() { + return signature(Boolean.class); + } + + @Override + public boolean isPointer() { + return true; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + if (type == this) { + // nothing to do + } else if (type == BOOL) { + environment.getOutput().invokeVirtual(Boolean.class, "booleanValue", boolean.class); + } else if (type == STRING) { + environment.getOutput().invokeVirtual(Boolean.class, "toString", String.class); + } else { + environment.getOutput().invokeVirtual(Boolean.class, "booleanValue", boolean.class); + BOOL.compileCast(position, environment, type); + } + } + + @Override + public String getName() { + return "bool"; + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type == BOOL || type == ZenTypeBoolObject.INSTANCE || type == STRING) { + return new ExpressionAs(position, value, type); + } else if (canCastExpansion(environment, type)) { + return castExpansion(position, environment, value, type); + } else { + return new ExpressionAs(position, value, type); + } + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionNull(position); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeByte.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeByte.java new file mode 100644 index 0000000..a74a91f --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeByte.java @@ -0,0 +1,175 @@ +package stanhebben.zenscript.type; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionArithmeticBinary; +import stanhebben.zenscript.expression.ExpressionArithmeticCompare; +import stanhebben.zenscript.expression.ExpressionArithmeticUnary; +import stanhebben.zenscript.expression.ExpressionAs; +import stanhebben.zenscript.expression.ExpressionInt; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import static stanhebben.zenscript.type.ZenType.STRING; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; +import stanhebben.zenscript.value.builtin.AnyByte; + +public class ZenTypeByte extends ZenType { + public static final ZenTypeByte INSTANCE = new ZenTypeByte(); + + private ZenTypeByte() {} + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + return null; + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + int itype = type.getNumberType(); + return itype == NUM_BYTE + || type == STRING + || canCastExpansion(environment, type); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return type.getNumberType() != 0 + || type == STRING + || canCastExpansion(environment, type); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type.getNumberType() > 0 || type == STRING) { + return new ExpressionAs(position, value, type); + } else if (canCastExpansion(environment, type)) { + return castExpansion(position, environment, value, type); + } else { + return new ExpressionAs(position, value, type); + } + } + + @Override + public Class toJavaClass() { + return byte.class; + } + + @Override + public Type toASMType() { + return Type.BYTE_TYPE; + } + + @Override + public int getNumberType() { + return NUM_BYTE; + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + IPartialExpression result = memberExpansion(position, environment, value.eval(environment), name); + if (result == null) { + environment.error(position, "bool value has no members"); + return new ExpressionInvalid(position, ZenTypeAny.INSTANCE); + } else { + return result; + } + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return null; + } + + @Override + public String getSignature() { + return "B"; + } + + @Override + public boolean isPointer() { + return false; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + MethodOutput output = environment.getOutput(); + + if (type == BYTE) { + // nothing to do + } else if (type == ZenTypeByteObject.INSTANCE) { + output.invokeStatic(Byte.class, "valueOf", Byte.class, byte.class); + } else if (type == SHORT) { + // nothing to do + } else if (type == ZenTypeShortObject.INSTANCE) { + output.invokeStatic(Short.class, "valueOf", Short.class, short.class); + } else if (type == INT) { + // nothing to do + } else if (type == ZenTypeIntObject.INSTANCE) { + output.invokeStatic(Integer.class, "valueOf", Integer.class, int.class); + } else if (type == LONG) { + output.i2l(); + } else if (type == ZenTypeLongObject.INSTANCE) { + output.i2l(); + output.invokeStatic(Long.class, "valueOf", Long.class, long.class); + } else if (type == FLOAT) { + output.i2f(); + } else if (type == ZenTypeFloatObject.INSTANCE) { + output.i2f(); + output.invokeStatic(Float.class, "valueOf", Float.class, float.class); + } else if (type == DOUBLE) { + output.i2d(); + } else if (type == ZenTypeDoubleObject.INSTANCE) { + output.i2d(); + output.invokeStatic(Double.class, "valueOf", Double.class, double.class); + } else if (type == STRING) { + output.invokeStatic(Byte.class, "toString", String.class, byte.class); + } else if (type == ANY) { + output.invokeStatic(AnyByte.class, "valueOf", AnyByte.class, byte.class); + } else if (!compileCastExpansion(position, environment, type)) { + environment.error(position, "cannot cast " + this + " to " + type); + } + } + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + return new ExpressionArithmeticUnary(position, operator, value); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + return new ExpressionArithmeticBinary(position, operator, left, right.cast(position, environment, this)); + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + environment.error(position, "byte doesn't support this operation"); + return new ExpressionInvalid(position); + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + return new ExpressionArithmeticCompare(position, type, left, right.cast(position, environment, this)); + } + + @Override + public Expression call( + ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + environment.error(position, "cannot call a byte value"); + return new ExpressionInvalid(position, ZenTypeByte.INSTANCE); + } + + @Override + public String getName() { + return "byte"; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionInt(position, 0, ZenType.BYTE); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeByteObject.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeByteObject.java new file mode 100644 index 0000000..e48af1b --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeByteObject.java @@ -0,0 +1,140 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionAs; +import stanhebben.zenscript.expression.ExpressionNull; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import static stanhebben.zenscript.type.ZenType.STRING; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.signature; + +/** + * + * @author Stan + */ +public class ZenTypeByteObject extends ZenType { + public static final ZenTypeByteObject INSTANCE = new ZenTypeByteObject(); + + private ZenTypeByteObject() {} + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + return BYTE.unary(position, environment, value.cast(position, environment, BYTE), operator); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + return BYTE.binary(position, environment, left.cast(position, environment, BYTE), right, operator); + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + return BYTE.trinary(position, environment, first.cast(position, environment, BYTE), second, third, operator); + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + return BYTE.compare(position, environment, left.cast(position, environment, BYTE), right, type); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + return BYTE.getMember(position, environment, value.eval(environment).cast(position, environment, BYTE), name); + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return BYTE.getStaticMember(position, environment, name); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + return BYTE.call(position, environment, receiver.cast(position, environment, BYTE), arguments); + } + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + return BYTE.makeIterator(numValues, methodOutput); + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return BYTE.canCastImplicit(type, environment); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type.getNumberType() > 0 || type == STRING) { + return new ExpressionAs(position, value, type); + } else if (canCastExpansion(environment, type)) { + return castExpansion(position, environment, value, type); + } else { + return new ExpressionAs(position, value, type); + } + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return BYTE.canCastExplicit(type, environment); + } + + @Override + public Class toJavaClass() { + return Byte.class; + } + + @Override + public Type toASMType() { + return Type.getType(Byte.class); + } + + @Override + public int getNumberType() { + return NUM_BYTE; + } + + @Override + public String getSignature() { + return signature(Byte.class); + } + + @Override + public boolean isPointer() { + return true; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + if (type == this) { + // nothing to do + } else if (type == BYTE) { + environment.getOutput().invokeVirtual(Byte.class, "byteValue", byte.class); + } else if (type == STRING) { + environment.getOutput().invokeVirtual(Byte.class, "toString", String.class); + } else { + environment.getOutput().invokeVirtual(Byte.class, "byteValue", byte.class); + BYTE.compileCast(position, environment, type); + } + } + + @Override + public String getName() { + return "byte"; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionNull(position); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeDouble.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeDouble.java new file mode 100644 index 0000000..caca7a8 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeDouble.java @@ -0,0 +1,182 @@ +package stanhebben.zenscript.type; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionArithmeticBinary; +import stanhebben.zenscript.expression.ExpressionArithmeticCompare; +import stanhebben.zenscript.expression.ExpressionArithmeticUnary; +import stanhebben.zenscript.expression.ExpressionAs; +import stanhebben.zenscript.expression.ExpressionFloat; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import static stanhebben.zenscript.type.ZenType.STRING; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +public class ZenTypeDouble extends ZenType { + public static final ZenTypeDouble INSTANCE = new ZenTypeDouble(); + + private ZenTypeDouble() {} + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + return null; + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return (type.getNumberType() != 0 && type.getNumberType() <= NUM_DOUBLE) + || type == STRING + || canCastExpansion(environment, type); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return type.getNumberType() != 0 + || type == STRING + || canCastExpansion(environment, type); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type.getNumberType() > 0 || type == STRING) { + return new ExpressionAs(position, value, type); + } else if (canCastExpansion(environment, type)) { + return castExpansion(position, environment, value, type); + } else { + return new ExpressionAs(position, value, type); + } + } + + @Override + public Type toASMType() { + return Type.DOUBLE_TYPE; + } + + @Override + public Class toJavaClass() { + return double.class; + } + + @Override + public int getNumberType() { + return NUM_DOUBLE; + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + IPartialExpression result = memberExpansion(position, environment, value.eval(environment), name); + if (result == null) { + environment.error(position, "double value has no members"); + return new ExpressionInvalid(position, ZenTypeAny.INSTANCE); + } else { + return result; + } + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return null; + } + + @Override + public String getSignature() { + return "D"; + } + + @Override + public boolean isPointer() { + return false; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + MethodOutput output = environment.getOutput(); + + if (type == BYTE) { + output.d2i(); + output.i2b(); + } else if (type == ZenTypeByteObject.INSTANCE) { + output.d2i(); + output.i2b(); + output.invokeStatic(Byte.class, "valueOf", Byte.class, byte.class); + } else if (type == SHORT) { + output.d2i(); + output.i2s(); + } else if (type == ZenTypeShortObject.INSTANCE) { + output.d2i(); + output.i2s(); + output.invokeStatic(Short.class, "valueOf", Short.class, short.class); + } else if (type == INT) { + output.d2i(); + } else if (type == ZenTypeIntObject.INSTANCE) { + output.d2i(); + output.invokeStatic(Integer.class, "valueOf", Integer.class, int.class); + } else if (type == LONG) { + output.d2l(); + } else if (type == ZenTypeLongObject.INSTANCE) { + output.d2l(); + output.invokeStatic(Long.class, "valueOf", Long.class, long.class); + } else if (type == FLOAT) { + output.d2f(); + } else if (type == ZenTypeFloatObject.INSTANCE) { + output.d2f(); + output.invokeStatic(Float.class, "valueOf", Float.class, float.class); + } else if (type == DOUBLE) { + // nothing to do + } else if (type == ZenTypeDoubleObject.INSTANCE) { + output.invokeStatic(Double.class, "valueOf", Double.class, double.class); + } else if (type == STRING) { + output.invokeStatic(Double.class, "toString", String.class, double.class); + } else if (!compileCastExpansion(position, environment, type)) { + environment.error(position, "cannot cast " + this + " to " + type); + } + } + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + return new ExpressionArithmeticUnary(position, operator, value); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + return new ExpressionArithmeticBinary(position, operator, left, right.cast(position, environment, this)); + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + environment.error(position, "double doesn't support this operation"); + return new ExpressionInvalid(position); + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + return new ExpressionArithmeticCompare(position, type, left, right.cast(position, environment, this)); + } + + @Override + public Expression call( + ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + environment.error(position, "cannot call a double value"); + return new ExpressionInvalid(position); + } + + @Override + public String getName() { + return "double"; + } + + @Override + public boolean isLarge() { + return true; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionFloat(position, 0.0, DOUBLE); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeDoubleObject.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeDoubleObject.java new file mode 100644 index 0000000..1831b4d --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeDoubleObject.java @@ -0,0 +1,140 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionAs; +import stanhebben.zenscript.expression.ExpressionNull; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import static stanhebben.zenscript.type.ZenType.STRING; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.signature; + +/** + * + * @author Stan + */ +public class ZenTypeDoubleObject extends ZenType { + public static final ZenTypeDoubleObject INSTANCE = new ZenTypeDoubleObject(); + + private ZenTypeDoubleObject() {} + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + return DOUBLE.unary(position, environment, value.cast(position, environment, DOUBLE), operator); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + return DOUBLE.binary(position, environment, left.cast(position, environment, DOUBLE), right, operator); + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + return DOUBLE.trinary(position, environment, first.cast(position, environment, DOUBLE), second, third, operator); + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + return DOUBLE.compare(position, environment, left.cast(position, environment, DOUBLE), right, type); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + return DOUBLE.getMember(position, environment, value.eval(environment).cast(position, environment, DOUBLE), name); + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return DOUBLE.getStaticMember(position, environment, name); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + return DOUBLE.call(position, environment, receiver.cast(position, environment, DOUBLE), arguments); + } + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + return DOUBLE.makeIterator(numValues, methodOutput); + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return DOUBLE.canCastImplicit(type, environment); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return DOUBLE.canCastExplicit(type, environment); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type.getNumberType() > 0 || type == STRING) { + return new ExpressionAs(position, value, type); + } else if (canCastExpansion(environment, type)) { + return castExpansion(position, environment, value, type); + } else { + return new ExpressionAs(position, value, type); + } + } + + @Override + public Class toJavaClass() { + return Double.class; + } + + @Override + public Type toASMType() { + return Type.getType(Double.class); + } + + @Override + public int getNumberType() { + return NUM_DOUBLE; + } + + @Override + public String getSignature() { + return signature(Double.class); + } + + @Override + public boolean isPointer() { + return true; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + if (type == this) { + // nothing to do + } else if (type == DOUBLE) { + environment.getOutput().invokeVirtual(Double.class, "doubleValue", double.class); + } else if (type == STRING) { + environment.getOutput().invokeVirtual(Double.class, "toString", String.class); + } else { + environment.getOutput().invokeVirtual(Double.class, "doubleValue", double.class); + DOUBLE.compileCast(position, environment, type); + } + } + + @Override + public String getName() { + return "double"; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionNull(position); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeFloat.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeFloat.java new file mode 100644 index 0000000..cfd423b --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeFloat.java @@ -0,0 +1,177 @@ +package stanhebben.zenscript.type; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionArithmeticBinary; +import stanhebben.zenscript.expression.ExpressionArithmeticCompare; +import stanhebben.zenscript.expression.ExpressionArithmeticUnary; +import stanhebben.zenscript.expression.ExpressionAs; +import stanhebben.zenscript.expression.ExpressionFloat; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import static stanhebben.zenscript.type.ZenType.STRING; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +public class ZenTypeFloat extends ZenType { + public static final ZenTypeFloat INSTANCE = new ZenTypeFloat(); + + private ZenTypeFloat() {} + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + return null; + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return (type.getNumberType() != 0 && type.getNumberType() <= NUM_FLOAT) + || type == ZenTypeString.INSTANCE + || canCastExpansion(environment, type); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return type.getNumberType() != 0 + || type == ZenTypeString.INSTANCE + || canCastExpansion(environment, type); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type.getNumberType() > 0 || type == STRING) { + return new ExpressionAs(position, value, type); + } else if (canCastExpansion(environment, type)) { + return castExpansion(position, environment, value, type); + } else { + return new ExpressionAs(position, value, type); + } + } + + @Override + public Type toASMType() { + return Type.FLOAT_TYPE; + } + + @Override + public int getNumberType() { + return NUM_FLOAT; + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + IPartialExpression result = memberExpansion(position, environment, value.eval(environment), name); + if (result == null) { + environment.error(position, "float value has no members"); + return new ExpressionInvalid(position, ZenTypeAny.INSTANCE); + } else { + return result; + } + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return null; + } + + @Override + public String getSignature() { + return "F"; + } + + @Override + public boolean isPointer() { + return false; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + MethodOutput output = environment.getOutput(); + + if (type == BYTE) { + output.f2i(); + output.i2b(); + } else if (type == ZenTypeByteObject.INSTANCE) { + output.f2i(); + output.i2b(); + output.invokeStatic(Byte.class, "valueOf", Byte.class, byte.class); + } else if (type == SHORT) { + output.f2i(); + output.i2s(); + } else if (type == ZenTypeShortObject.INSTANCE) { + output.f2i(); + output.i2s(); + output.invokeStatic(Short.class, "valueOf", Short.class, short.class); + } else if (type == INT) { + output.f2i(); + } else if (type == ZenTypeIntObject.INSTANCE) { + output.f2i(); + output.invokeStatic(Integer.class, "valueOf", Integer.class, int.class); + } else if (type == LONG) { + output.f2l(); + } else if (type == ZenTypeLongObject.INSTANCE) { + output.f2l(); + output.invokeStatic(Long.class, "valueOf", Long.class, long.class); + } else if (type == FLOAT) { + // nothing to do + } else if (type == ZenTypeFloatObject.INSTANCE) { + output.invokeStatic(Float.class, "valueOf", Float.class, float.class); + } else if (type == DOUBLE) { + output.f2d(); + } else if (type == ZenTypeDoubleObject.INSTANCE) { + output.f2d(); + output.invokeStatic(Double.class, "valueOf", Double.class, double.class); + } else if (type == STRING) { + output.invokeStatic(Float.class, "toString", String.class, float.class); + } else if (!compileCastExpansion(position, environment, type)) { + environment.error(position, "cannot cast " + this + " to " + type); + } + } + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + return new ExpressionArithmeticUnary(position, operator, value); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + return new ExpressionArithmeticBinary(position, operator, left, right.cast(position, environment, this)); + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + environment.error(position, "float doesn't support this operation"); + return new ExpressionInvalid(position); + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + return new ExpressionArithmeticCompare(position, type, left, right.cast(position, environment, this)); + } + + @Override + public Expression call( + ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + environment.error(position, "cannot call a float value"); + return new ExpressionInvalid(position); + } + + @Override + public Class toJavaClass() { + return float.class; + } + + @Override + public String getName() { + return "float"; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionFloat(position, 0.0, FLOAT); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeFloatObject.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeFloatObject.java new file mode 100644 index 0000000..b8bffa4 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeFloatObject.java @@ -0,0 +1,140 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionAs; +import stanhebben.zenscript.expression.ExpressionNull; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import static stanhebben.zenscript.type.ZenType.STRING; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.signature; + +/** + * + * @author Stan + */ +public class ZenTypeFloatObject extends ZenType { + public static final ZenTypeFloatObject INSTANCE = new ZenTypeFloatObject(); + + private ZenTypeFloatObject() {} + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + return FLOAT.unary(position, environment, value.cast(position, environment, FLOAT), operator); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + return FLOAT.binary(position, environment, left.cast(position, environment, FLOAT), right, operator); + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + return FLOAT.trinary(position, environment, first.cast(position, environment, FLOAT), second, third, operator); + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + return FLOAT.compare(position, environment, left.cast(position, environment, FLOAT), right, type); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + return FLOAT.getMember(position, environment, value.eval(environment).cast(position, environment, FLOAT), name); + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return FLOAT.getStaticMember(position, environment, name); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + return FLOAT.call(position, environment, receiver.cast(position, environment, FLOAT), arguments); + } + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + return FLOAT.makeIterator(numValues, methodOutput); + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return FLOAT.canCastImplicit(type, environment); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return FLOAT.canCastExplicit(type, environment); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type.getNumberType() > 0 || type == STRING) { + return new ExpressionAs(position, value, type); + } else if (canCastExpansion(environment, type)) { + return castExpansion(position, environment, value, type); + } else { + return new ExpressionAs(position, value, type); + } + } + + @Override + public Class toJavaClass() { + return Float.class; + } + + @Override + public Type toASMType() { + return Type.getType(Float.class); + } + + @Override + public int getNumberType() { + return NUM_FLOAT; + } + + @Override + public String getSignature() { + return signature(Float.class); + } + + @Override + public boolean isPointer() { + return true; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + if (type == this) { + // nothing to do + } else if (type == FLOAT) { + environment.getOutput().invokeVirtual(Float.class, "floatValue", float.class); + } else if (type == STRING) { + environment.getOutput().invokeVirtual(Float.class, "toString", String.class); + } else { + environment.getOutput().invokeVirtual(Float.class, "floatValue", float.class); + FLOAT.compileCast(position, environment, type); + } + } + + @Override + public String getName() { + return "float"; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionNull(position); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeFunction.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeFunction.java new file mode 100644 index 0000000..c5335cd --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeFunction.java @@ -0,0 +1,178 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type; + +import java.util.List; +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.definitions.ParsedFunctionArgument; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.ExpressionNull; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ZenTypeFunction extends ZenType { + private final ZenType returnType; + private final ZenType[] argumentTypes; + private final String name; + + public ZenTypeFunction(ZenType returnType, List arguments) { + this.returnType = returnType; + argumentTypes = new ZenType[arguments.size()]; + for (int i = 0; i < argumentTypes.length; i++) { + argumentTypes[i] = arguments.get(i).getType(); + } + + StringBuilder nameBuilder = new StringBuilder(); + nameBuilder.append("function("); + for (ZenType type : argumentTypes) { + nameBuilder.append(type.getName()); + } + nameBuilder.append(returnType.getName()); + name = nameBuilder.toString(); + } + + public ZenTypeFunction(ZenType returnType, ZenType[] argumentTypes) { + this.returnType = returnType; + this.argumentTypes = argumentTypes; + + StringBuilder nameBuilder = new StringBuilder(); + nameBuilder.append("function("); + for (ZenType type : argumentTypes) { + nameBuilder.append(type.getName()); + } + nameBuilder.append(returnType.getName()); + name = nameBuilder.toString(); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + environment.error(position, "Functions have no members"); + return new ExpressionInvalid(position, ZenTypeAny.INSTANCE); + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + environment.error(position, "Functions have no static members"); + return new ExpressionInvalid(position, ZenTypeAny.INSTANCE); + } + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + return null; + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return equals(type) || canCastToNative(type) || canCastExpansion(environment, type); // TODO: LATER: expand + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return equals(type) || canCastToNative(type) || canCastExpansion(environment, type); // TODO: LATER: expand + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + throw new UnsupportedOperationException("not supported yet"); + } + + @Override + public Type toASMType() { + return null; // TODO: NEXT: expand + } + + @Override + public int getNumberType() { + return 0; + } + + @Override + public String getSignature() { + return null; // TODO: NEXT: expand + } + + @Override + public boolean isPointer() { + return true; + } + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + environment.error(position, "cannot apply operators on a function"); + return new ExpressionInvalid(position, ZenTypeAny.INSTANCE); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + environment.error(position, "cannot apply operators on a function"); + return new ExpressionInvalid(position, ZenTypeAny.INSTANCE); + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + environment.error(position, "cannot apply operators on a function"); + return new ExpressionInvalid(position, ZenTypeAny.INSTANCE); + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + environment.error(position, "cannot apply operators on a function"); + return new ExpressionInvalid(position, ZenTypeAny.INSTANCE); + } + + @Override + public Expression call( + ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + return null; // TODO: complete + } + + @Override + public Class toJavaClass() { + // TODO: complete + return null; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + if (!compileCastExpansion(position, environment, type)) { + environment.error(position, "cannot cast " + this + " to " + type); + } + } + + @Override + public String getName() { + return name; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionNull(position); + } + + private boolean canCastToNative(ZenType type) { + if (!(type instanceof ZenTypeNative)) return false; + + ZenTypeNative ntype = (ZenTypeNative) type; + if (ntype.getNativeClass().isInterface() && ntype.getNativeClass().getMethods().length == 1) { + // ta-da! we got a functional interface + // does it match? + // TODO: expand + return true; // assume for now it is + } + + return false; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeInt.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeInt.java new file mode 100644 index 0000000..1d17399 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeInt.java @@ -0,0 +1,173 @@ +package stanhebben.zenscript.type; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionArithmeticBinary; +import stanhebben.zenscript.expression.ExpressionArithmeticCompare; +import stanhebben.zenscript.expression.ExpressionArithmeticUnary; +import stanhebben.zenscript.expression.ExpressionAs; +import stanhebben.zenscript.expression.ExpressionInt; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import static stanhebben.zenscript.type.ZenType.STRING; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +public class ZenTypeInt extends ZenType { + public static final ZenTypeInt INSTANCE = new ZenTypeInt(); + + private ZenTypeInt() {} + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + return null; + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return (type.getNumberType() != 0 && type.getNumberType() <= NUM_INT) + || type == ZenTypeString.INSTANCE + || canCastExpansion(environment, type); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return type.getNumberType() != 0 + || type == ZenTypeString.INSTANCE + || canCastExpansion(environment, type); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type.getNumberType() > 0 || type == STRING) { + return new ExpressionAs(position, value, type); + } else if (canCastExpansion(environment, type)) { + return castExpansion(position, environment, value, type); + } else { + return new ExpressionAs(position, value, type); + } + } + + @Override + public Type toASMType() { + return Type.INT_TYPE; + } + + @Override + public int getNumberType() { + return NUM_INT; + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + IPartialExpression result = memberExpansion(position, environment, value.eval(environment), name); + if (result == null) { + environment.error(position, "bool value has no members"); + return new ExpressionInvalid(position, ZenTypeAny.INSTANCE); + } else { + return result; + } + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return null; + } + + @Override + public String getSignature() { + return "I"; + } + + @Override + public boolean isPointer() { + return false; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + MethodOutput output = environment.getOutput(); + + if (type == BYTE) { + output.i2b(); + } else if (type == ZenTypeByteObject.INSTANCE) { + output.i2b(); + output.invokeStatic(Byte.class, "valueOf", Byte.class, byte.class); + } else if (type == SHORT) { + output.i2s(); + } else if (type == ZenTypeShortObject.INSTANCE) { + output.i2s(); + output.invokeStatic(Short.class, "valueOf", Short.class, short.class); + } else if (type == INT) { + // nothing to do + } else if (type == ZenTypeIntObject.INSTANCE) { + output.invokeStatic(Integer.class, "valueOf", Integer.class, int.class); + } else if (type == LONG) { + output.i2l(); + } else if (type == ZenTypeLongObject.INSTANCE) { + output.i2l(); + output.invokeStatic(Long.class, "valueOf", Long.class, long.class); + } else if (type == FLOAT) { + output.i2f(); + } else if (type == ZenTypeFloatObject.INSTANCE) { + output.i2f(); + output.invokeStatic(Float.class, "valueOf", Float.class, float.class); + } else if (type == DOUBLE) { + output.i2d(); + } else if (type == ZenTypeDoubleObject.INSTANCE) { + output.i2d(); + output.invokeStatic(Double.class, "valueOf", Double.class, double.class); + } else if (type == STRING) { + output.invokeStatic(Integer.class, "toString", String.class, int.class); + } else if (!compileCastExpansion(position, environment, type)) { + environment.error(position, "cannot cast " + this + " to " + type); + } + } + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + return new ExpressionArithmeticUnary(position, operator, value); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + return new ExpressionArithmeticBinary(position, operator, left, right.cast(position, environment, this)); + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + environment.error(position, "int doesn't support this operation"); + return new ExpressionInvalid(position); + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + return new ExpressionArithmeticCompare(position, type, left, right.cast(position, environment, this)); + } + + @Override + public Expression call( + ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + environment.error(position, "cannot call integer values"); + return new ExpressionInvalid(position); + } + + @Override + public Class toJavaClass() { + return int.class; + } + + @Override + public String getName() { + return "int"; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionInt(position, 0, INT); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeIntObject.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeIntObject.java new file mode 100644 index 0000000..688f7d2 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeIntObject.java @@ -0,0 +1,140 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionAs; +import stanhebben.zenscript.expression.ExpressionNull; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import static stanhebben.zenscript.type.ZenType.STRING; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.signature; + +/** + * + * @author Stan + */ +public class ZenTypeIntObject extends ZenType { + public static final ZenTypeIntObject INSTANCE = new ZenTypeIntObject(); + + private ZenTypeIntObject() {} + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + return INT.unary(position, environment, value.cast(position, environment, INT), operator); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + return INT.binary(position, environment, left.cast(position, environment, INT), right, operator); + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + return INT.trinary(position, environment, first.cast(position, environment, INT), second, third, operator); + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + return INT.compare(position, environment, left.cast(position, environment, INT), right, type); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + return INT.getMember(position, environment, value.eval(environment).cast(position, environment, INT), name); + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return INT.getStaticMember(position, environment, name); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + return INT.call(position, environment, receiver.cast(position, environment, INT), arguments); + } + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + return INT.makeIterator(numValues, methodOutput); + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return INT.canCastImplicit(type, environment); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return INT.canCastExplicit(type, environment); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type.getNumberType() > 0 || type == STRING) { + return new ExpressionAs(position, value, type); + } else if (canCastExpansion(environment, type)) { + return castExpansion(position, environment, value, type); + } else { + return new ExpressionAs(position, value, type); + } + } + + @Override + public Class toJavaClass() { + return Integer.class; + } + + @Override + public Type toASMType() { + return Type.getType(Integer.class); + } + + @Override + public int getNumberType() { + return NUM_INT; + } + + @Override + public String getSignature() { + return signature(Integer.class); + } + + @Override + public boolean isPointer() { + return true; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + if (type == this) { + // nothing to do + } else if (type == INT) { + environment.getOutput().invokeVirtual(Integer.class, "intValue", int.class); + } else if (type == STRING) { + environment.getOutput().invokeVirtual(Integer.class, "toString", String.class); + } else { + environment.getOutput().invokeVirtual(Integer.class, "intValue", int.class); + INT.compileCast(position, environment, type); + } + } + + @Override + public String getName() { + return "int"; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionNull(position); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeLong.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeLong.java new file mode 100644 index 0000000..999be92 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeLong.java @@ -0,0 +1,182 @@ +package stanhebben.zenscript.type; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionArithmeticBinary; +import stanhebben.zenscript.expression.ExpressionArithmeticCompare; +import stanhebben.zenscript.expression.ExpressionArithmeticUnary; +import stanhebben.zenscript.expression.ExpressionAs; +import stanhebben.zenscript.expression.ExpressionInt; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import static stanhebben.zenscript.type.ZenType.STRING; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +public class ZenTypeLong extends ZenType { + public static final ZenTypeLong INSTANCE = new ZenTypeLong(); + + private ZenTypeLong() {} + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + return null; + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return (type.getNumberType() != 0 && type.getNumberType() <= NUM_LONG) + || type == ZenTypeString.INSTANCE + || canCastExpansion(environment, type); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return type.getNumberType() != 0 + || type == ZenTypeString.INSTANCE + || canCastExpansion(environment, type); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type.getNumberType() > 0 || type == STRING) { + return new ExpressionAs(position, value, type); + } else if (canCastExpansion(environment, type)) { + return castExpansion(position, environment, value, type); + } else { + return new ExpressionAs(position, value, type); + } + } + + @Override + public Type toASMType() { + return Type.LONG_TYPE; + } + + @Override + public int getNumberType() { + return NUM_LONG; + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + IPartialExpression result = memberExpansion(position, environment, value.eval(environment), name); + if (result == null) { + environment.error(position, "bool value has no members"); + return new ExpressionInvalid(position, ZenTypeAny.INSTANCE); + } else { + return result; + } + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return null; + } + + @Override + public String getSignature() { + return "J"; + } + + @Override + public boolean isPointer() { + return false; + } + + @Override + public void compileCast(stanhebben.zenscript.util.ZenPosition position, IEnvironmentMethod environment, ZenType type) { + MethodOutput output = environment.getOutput(); + + if (type == BYTE) { + output.l2i(); + output.i2b(); + } else if (type == ZenTypeByteObject.INSTANCE) { + output.l2i(); + output.i2b(); + output.invokeStatic(Byte.class, "valueOf", Byte.class, byte.class); + } else if (type == SHORT) { + output.l2i(); + output.i2s(); + } else if (type == ZenTypeShortObject.INSTANCE) { + output.l2i(); + output.i2s(); + output.invokeStatic(Short.class, "valueOf", Short.class, short.class); + } else if (type == INT) { + output.l2i(); + } else if (type == ZenTypeIntObject.INSTANCE) { + output.l2i(); + output.invokeStatic(Integer.class, "valueOf", Integer.class, int.class); + } else if (type == LONG) { + // nothing to do + } else if (type == ZenTypeLongObject.INSTANCE) { + output.invokeStatic(Long.class, "valueOf", Long.class, long.class); + } else if (type == FLOAT) { + output.l2f(); + } else if (type == ZenTypeFloatObject.INSTANCE) { + output.l2f(); + output.invokeStatic(Float.class, "valueOf", Float.class, float.class); + } else if (type == DOUBLE) { + output.l2d(); + } else if (type == ZenTypeDoubleObject.INSTANCE) { + output.l2d(); + output.invokeStatic(Double.class, "valueOf", Double.class, double.class); + } else if (type == STRING) { + output.invokeStatic(Long.class, "toString", String.class, long.class); + } else if (!compileCastExpansion(position, environment, type)) { + environment.error(position, "cannot cast " + this + " to " + type); + } + } + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + return new ExpressionArithmeticUnary(position, operator, value); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + return new ExpressionArithmeticBinary(position, operator, left, right.cast(position, environment, this)); + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + environment.error(position, "long doesn't support this operation"); + return new ExpressionInvalid(position); + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + return new ExpressionArithmeticCompare(position, type, left, right.cast(position, environment, this)); + } + + @Override + public Expression call( + ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + environment.error(position, "cannot call a long value"); + return new ExpressionInvalid(position); + } + + @Override + public Class toJavaClass() { + return long.class; + } + + @Override + public String getName() { + return "name"; + } + + @Override + public boolean isLarge() { + return true; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionInt(position, 0, LONG); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeLongObject.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeLongObject.java new file mode 100644 index 0000000..3556fbc --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeLongObject.java @@ -0,0 +1,140 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionAs; +import stanhebben.zenscript.expression.ExpressionNull; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import static stanhebben.zenscript.type.ZenType.STRING; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.signature; + +/** + * + * @author Stan + */ +public class ZenTypeLongObject extends ZenType { + public static final ZenTypeLongObject INSTANCE = new ZenTypeLongObject(); + + private ZenTypeLongObject() {} + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + return LONG.unary(position, environment, value.cast(position, environment, LONG), operator); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + return LONG.binary(position, environment, left.cast(position, environment, LONG), right, operator); + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + return LONG.trinary(position, environment, first.cast(position, environment, LONG), second, third, operator); + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + return LONG.compare(position, environment, left.cast(position, environment, LONG), right, type); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + return LONG.getMember(position, environment, value.eval(environment).cast(position, environment, LONG), name); + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return LONG.getStaticMember(position, environment, name); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + return LONG.call(position, environment, receiver.cast(position, environment, LONG), arguments); + } + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + return LONG.makeIterator(numValues, methodOutput); + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return LONG.canCastImplicit(type, environment); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return LONG.canCastExplicit(type, environment); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type.getNumberType() > 0 || type == STRING) { + return new ExpressionAs(position, value, type); + } else if (canCastExpansion(environment, type)) { + return castExpansion(position, environment, value, type); + } else { + return new ExpressionAs(position, value, type); + } + } + + @Override + public Class toJavaClass() { + return Long.class; + } + + @Override + public Type toASMType() { + return Type.getType(Long.class); + } + + @Override + public int getNumberType() { + return NUM_LONG; + } + + @Override + public String getSignature() { + return signature(Long.class); + } + + @Override + public boolean isPointer() { + return true; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + if (type == this) { + // nothing to do + } else if (type == LONG) { + environment.getOutput().invokeVirtual(Long.class, "longValue", long.class); + } else if (type == STRING) { + environment.getOutput().invokeVirtual(Long.class, "toString", String.class); + } else { + environment.getOutput().invokeVirtual(Long.class, "longValue", long.class); + LONG.compileCast(position, environment, type); + } + } + + @Override + public String getName() { + return "long"; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionNull(position); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeNative.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeNative.java new file mode 100644 index 0000000..56ec70e --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeNative.java @@ -0,0 +1,962 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import org.objectweb.asm.ClassWriter; +import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import stanhebben.zenscript.ZenRuntimeException; +import stanhebben.zenscript.annotations.ZenCaster; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.ZenClass; +import stanhebben.zenscript.annotations.ZenGetter; +import stanhebben.zenscript.annotations.IterableList; +import stanhebben.zenscript.annotations.IterableMap; +import stanhebben.zenscript.annotations.IterableSimple; +import stanhebben.zenscript.annotations.ZenOperator; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.annotations.ZenMemberGetter; +import stanhebben.zenscript.annotations.ZenMemberSetter; +import stanhebben.zenscript.annotations.ZenSetter; +import stanhebben.zenscript.annotations.ZenMethod; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.compiler.EnvironmentMethod; +import stanhebben.zenscript.compiler.ITypeRegistry; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionArithmeticUnary; +import stanhebben.zenscript.expression.ExpressionAs; +import stanhebben.zenscript.expression.ExpressionCompareGeneric; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.ExpressionNull; +import stanhebben.zenscript.expression.ExpressionString; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.type.iterator.IteratorIterable; +import stanhebben.zenscript.type.iterator.IteratorList; +import stanhebben.zenscript.type.iterator.IteratorMap; +import stanhebben.zenscript.type.iterator.IteratorMapKeys; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.type.natives.ZenNativeCaster; +import stanhebben.zenscript.type.natives.ZenNativeMember; +import stanhebben.zenscript.type.natives.ZenNativeOperator; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.signature; +import stanhebben.zenscript.value.IAny; + +/** + * + * @author Stanneke + */ +public class ZenTypeNative extends ZenType { + private static final int ITERATOR_NONE = 0; + private static final int ITERATOR_ITERABLE = 1; + private static final int ITERATOR_LIST = 2; + private static final int ITERATOR_MAP = 3; + + private final Class cls; + private final List implementing; + private final Map members; + private final Map staticMembers; + private final List casters; + private final List trinaryOperators; + private final List binaryOperators; + private final List unaryOperators; + + private int iteratorType; + private String classPkg; + private String className; + private Annotation iteratorAnnotation; + private ZenType iteratorKeyType; + private ZenType iteratorValueType; + + public ZenTypeNative(Class cls) { + this.cls = cls; + members = new HashMap(); + staticMembers = new HashMap(); + casters = new ArrayList(); + trinaryOperators = new ArrayList(); + binaryOperators = new ArrayList(); + unaryOperators = new ArrayList(); + implementing = new ArrayList(); + } + + public void complete(ITypeRegistry types) { + int iterator = ITERATOR_NONE; + Annotation _iteratorAnnotation = null; + String _classPkg = cls.getPackage().getName().replace('/', '.'); + String _className = cls.getSimpleName(); + boolean fully = false; + + Queue todo = new LinkedList(); + todo.add(this); + addSubtypes(todo, types); + + Annotation[] clsAnnotations = cls.getAnnotations(); + for (Annotation annotation : clsAnnotations) { + if (annotation instanceof ZenClass) { + String value = ((ZenClass) annotation).value(); + int dot = value.lastIndexOf('.'); + if (dot < 0) { + _classPkg = null; + _className = value; + } else { + _classPkg = value.substring(0, dot); + _className = value.substring(dot + 1); + } + } + if (annotation instanceof IterableSimple) { + iterator = ITERATOR_ITERABLE; + _iteratorAnnotation = annotation; + if (!Iterable.class.isAssignableFrom(cls)) { + // TODO: illegal + } + } + if (annotation instanceof IterableList) { + iterator = ITERATOR_LIST; + _iteratorAnnotation = annotation; + if (!List.class.isAssignableFrom(cls)) { + // TODO: illegal + } + } + if (annotation instanceof IterableMap) { + iterator = ITERATOR_MAP; + _iteratorAnnotation = annotation; + if (!Map.class.isAssignableFrom(cls)) { + // TODO: illegal + } + } + } + + for (Method method : cls.getMethods()) { + boolean isMethod = fully; + String methodName = method.getName(); + + for (Annotation annotation : method.getAnnotations()) { + if (annotation instanceof ZenCaster) { + Class output = method.getReturnType(); + casters.add(new ZenNativeCaster(cls, output, method.getName())); + isMethod = false; + } else if (annotation instanceof ZenGetter) { + ZenGetter getterAnnotation = (ZenGetter) annotation; + String name = getterAnnotation.value().length() == 0 ? method.getName() : getterAnnotation.value(); + + if (!members.containsKey(name)) { + members.put(name, new ZenNativeMember()); + } + members.get(name).setGetter(new JavaMethod(cls, method, types)); + isMethod = false; + } else if (annotation instanceof ZenSetter) { + ZenSetter setterAnnotation = (ZenSetter) annotation; + String name = setterAnnotation.value().length() == 0 ? method.getName() : setterAnnotation.value(); + + if (!members.containsKey(name)) { + members.put(name, new ZenNativeMember()); + } + members.get(name).setSetter(new JavaMethod(cls, method, types)); + isMethod = false; + } else if (annotation instanceof ZenMemberGetter) { + binaryOperators.add(new ZenNativeOperator(OperatorType.MEMBERGETTER, new JavaMethod(cls, method, types))); + } else if (annotation instanceof ZenMemberSetter) { + trinaryOperators.add(new ZenNativeOperator(OperatorType.MEMBERSETTER, new JavaMethod(cls, method, types))); + } else if (annotation instanceof ZenOperator) { + ZenOperator operatorAnnotation = (ZenOperator) annotation; + switch (operatorAnnotation.value()) { + case NEG: + case NOT: + if (method.getParameterTypes().length != 0) { + // TODO: error + } else { + unaryOperators.add(new ZenNativeOperator( + operatorAnnotation.value(), + new JavaMethod(cls, method, types))); + } + break; + case ADD: + case SUB: + case CAT: + case MUL: + case DIV: + case MOD: + case AND: + case OR: + case XOR: + case INDEXGET: + case RANGE: + case CONTAINS: + case COMPARE: + if (method.getParameterTypes().length != 1) { + // TODO: error + } else { + binaryOperators.add(new ZenNativeOperator( + operatorAnnotation.value(), + new JavaMethod(cls, method, types))); + } + break; + case INDEXSET: + if (method.getParameterTypes().length != 2) { + // TODO: error + } else { + trinaryOperators.add(new ZenNativeOperator( + operatorAnnotation.value(), + new JavaMethod(cls, method, types))); + } + break; + } + isMethod = false; + } else if (annotation instanceof ZenMethod) { + isMethod = true; + + ZenMethod methodAnnotation = (ZenMethod) annotation; + if (methodAnnotation.value().length() > 0) { + methodName = methodAnnotation.value(); + } + } + } + + if (isMethod) { + if ((method.getModifiers() & Modifier.STATIC) > 0) { + if (!staticMembers.containsKey(methodName)) { + staticMembers.put(methodName, new ZenNativeMember()); + } + staticMembers.get(methodName).addMethod(new JavaMethod(cls, method, types)); + } else { + if (!members.containsKey(methodName)) { + members.put(methodName, new ZenNativeMember()); + } + members.get(methodName).addMethod(new JavaMethod(cls, method, types)); + } + } + } + + this.iteratorType = iterator; + this.iteratorAnnotation = _iteratorAnnotation; + this.classPkg = _classPkg; + this.className = _className; + } + + public Class getNativeClass() { + return cls; + } + + public void complete(IEnvironmentGlobal environment) { + if (iteratorAnnotation instanceof IterableSimple) { + IterableSimple annotation = (IterableSimple) iteratorAnnotation; + iteratorValueType = ZenType.parse(annotation.value(), environment); + } + if (iteratorAnnotation instanceof IterableList) { + IterableList annotation = (IterableList) iteratorAnnotation; + iteratorKeyType = ZenTypeInt.INSTANCE; + iteratorValueType = ZenType.parse(annotation.value(), environment); + } + if (iteratorAnnotation instanceof IterableMap) { + IterableMap annotation = (IterableMap) iteratorAnnotation; + iteratorKeyType = ZenType.parse(annotation.key(), environment); + iteratorValueType = ZenType.parse(annotation.value(), environment); + } + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + ZenNativeMember member = members.get(name); + if (member == null) { + for (ZenTypeNative type : implementing) { + if (type.members.containsKey(name)) { + member = type.members.get(name); + break; + } + } + } + if (member == null) { + Expression evalue = value.eval(environment); + IPartialExpression member2 = memberExpansion(position, environment, evalue, name); + if (member2 == null) { + for (ZenTypeNative type : implementing) { + member2 = type.memberExpansion(position, environment, evalue, name); + if (member2 != null) break; + } + } + if (member2 == null) { + return binary( + position, + environment, + value.eval(environment), + new ExpressionString(position, name), + OperatorType.MEMBERGETTER); + } else { + return member2; + } + } else { + return member.instance(position, environment, value); + } + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + ZenNativeMember member = staticMembers.get(name); + if (member == null) { + for (ZenTypeNative type : implementing) { + if (type.staticMembers.containsKey(name)) { + member = type.staticMembers.get(name); + break; + } + } + } + if (member == null) { + IPartialExpression member2 = staticMemberExpansion(position, environment, name); + if (member2 == null) { + for (ZenTypeNative type : implementing) { + member2 = type.staticMemberExpansion(position, environment, name); + if (member2 != null) break; + } + } + if (member2 == null) { + environment.error(position, "No such static member in " + getName() + ": " + name); + return new ExpressionInvalid(position); + } else { + return member2; + } + } else { + return member.instance(position, environment); + } + } + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + switch (iteratorType) { + case ITERATOR_NONE: + break; + case ITERATOR_ITERABLE: + if (numValues == 1) { + return new IteratorIterable(methodOutput.getOutput(), iteratorValueType); + } else if (numValues == 2) { + return new IteratorList(methodOutput.getOutput(), iteratorValueType); + } + break; + case ITERATOR_MAP: + if (numValues == 1) { + return new IteratorMapKeys(methodOutput.getOutput(), new ZenTypeAssociative(iteratorValueType, iteratorKeyType)); + } else if (numValues == 2) { + return new IteratorMap(methodOutput.getOutput(), new ZenTypeAssociative(iteratorValueType, iteratorKeyType)); + } + break; + case ITERATOR_LIST: + if (numValues == 1) { + // list is also iterable + return new IteratorIterable(methodOutput.getOutput(), iteratorValueType); + } else if (numValues == 2) { + return new IteratorList(methodOutput.getOutput(), iteratorValueType); + } + break; + } + return null; + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return type == this + || canCastImplicit(cls, type) + || canCastExpansion(environment, type); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return type == this + || canCastImplicit(cls, type) + || canCastImplicit(type, cls) + || canCastExpansion(environment, type); + } + + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type == this || canCastImplicit(cls, type)) { + return value; + } + + if (canCastExpansion(environment, type)) { + return castExpansion(position, environment, value, type); + } else { + return new ExpressionAs(position, value, type); + } + } + + @Override + public Class toJavaClass() { + return cls; + } + + @Override + public Type toASMType() { + return Type.getType(cls); + } + + @Override + public int getNumberType() { + return 0; + } + + @Override + public String getSignature() { + return cls.getName(); + } + + @Override + public boolean isPointer() { + return true; + } + + /** + * Can we cast the given class to the given type implicitly? + * + * @param cls class to be casted + * @param type type to cast to + * @return true if casting is possible + */ + private static boolean canCastImplicit(Class cls, ZenType type) { + if (isEqual(cls, type)) return true; + if (canCastImplicit(cls.getSuperclass(), type)) return true; + for (Class iface : cls.getInterfaces()) { + if (canCastImplicit(iface, type)) return true; + } + return false; + } + + /** + * Can we cast the given type to the given class implicitly? + * + * @param type type to be casted + * @param cls type to cast to + * @return true if casting is possible + */ + private static boolean canCastImplicit(ZenType type, Class cls) { + if (isEqual(cls, type)) return true; + if (type instanceof ZenTypeNative) { + Class clsFrom = ((ZenTypeNative) type).cls; + return cls.isAssignableFrom(clsFrom); + } + return false; + } + + private static boolean isEqual(Class cls, ZenType type) { + String signature = getSignature(cls); + return type.getSignature().equals(signature); + } + + private static String getSignature(Class cls) { + String signature = cls.getName(); + if (Number.class.isAssignableFrom(cls)) { + if (cls == Byte.class) { + signature = "B"; + } else if (cls == Short.class) { + signature = "S"; + } else if (cls == Integer.class) { + signature = "I"; + } else if (cls == Long.class) { + signature = "J"; + } else if (cls == Float.class) { + signature = "F"; + } else if (cls == Double.class) { + signature = "D"; + } + } + return signature; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType toType) { + if (toType == ZenTypeAny.INSTANCE) { + String anyClassName = this.cls.getName() + "__Any"; + String anySignature = signature(IAny.class); + String toSignature = "L" + anyClassName + ""; + + if (!environment.containsClass(anyClassName)) { + environment.putClass(anyClassName, new byte[0]); // prevent recursive issues + + ClassWriter newClass = new ClassWriter(COMPUTE_FRAMES); + newClass.visit( + Opcodes.V1_6, + Opcodes.ACC_PUBLIC, + anyClassName, + "L" + anyClassName + ";", + signature(Object.class), + new String[] { anySignature }); + + newClass.visitField(Opcodes.ACC_PRIVATE, "value", null, className, null); + + // static valueOf method + MethodOutput valueOfMethod = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, + "valueOf", + "(" + getSignature() + ")L" + anyClassName + ";", + null, + null); + valueOfMethod.start(); + valueOfMethod.newObject(anyClassName); + valueOfMethod.loadObject(0); + valueOfMethod.construct(anyClassName, getSignature()); + valueOfMethod.returnObject(); + valueOfMethod.end(); + + // single constructor + MethodOutput constructorMethod = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "", + "(" + getSignature() + ")V", + null, + null); + constructorMethod.start(); + constructorMethod.loadObject(1); + constructorMethod.loadObject(0); + constructorMethod.putField(toSignature, "value", null); + constructorMethod.end(); + + // implementation for each of the interface methods + MethodOutput methodNot = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "not", + "()" + anySignature, + null, + null); + methodNot.start(); + compileAnyUnary( + anySignature, + OperatorType.NOT, + new EnvironmentMethod(methodNot, environment)); + methodNot.end(); + + MethodOutput methodNeg = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "neg", + "()" + anySignature, + null, + null); + methodNeg.start(); + compileAnyUnary( + anySignature, + OperatorType.NEG, + new EnvironmentMethod(methodNeg, environment)); + methodNeg.end(); + + MethodOutput methodAdd = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "add", + "(" + signature(IAny.class) + ")" + anySignature, + null, + null); + methodAdd.start(); + compileAnyBinary( + anySignature, + OperatorType.ADD, + new EnvironmentMethod(methodAdd, environment)); + methodAdd.end(); + + MethodOutput methodSub = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "sub", + "(" + signature(IAny.class) + ")" + anySignature, + null, + null); + methodSub.start(); + compileAnyBinary( + anySignature, + OperatorType.SUB, + new EnvironmentMethod(methodSub, environment)); + methodSub.end(); + + MethodOutput methodCat = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "cat", + "(" + signature(IAny.class) + ")" + anySignature, + null, + null); + methodCat.start(); + compileAnyBinary( + anySignature, + OperatorType.CAT, + new EnvironmentMethod(methodCat, environment)); + methodCat.end(); + + MethodOutput methodMul = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "mul", + "(" + signature(IAny.class) + ")" + anySignature, + null, + null); + methodMul.start(); + compileAnyBinary( + anySignature, + OperatorType.ADD, + new EnvironmentMethod(methodMul, environment)); + methodMul.end(); + + MethodOutput methodDiv = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "div", + "(" + signature(IAny.class) + ")" + anySignature, + null, + null); + methodDiv.start(); + compileAnyBinary( + anySignature, + OperatorType.DIV, + new EnvironmentMethod(methodDiv, environment)); + methodDiv.end(); + + MethodOutput methodMod = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "mod", + "(" + signature(IAny.class) + ")" + anySignature, + null, + null); + methodMod.start(); + compileAnyBinary( + anySignature, + OperatorType.MOD, + new EnvironmentMethod(methodMod, environment)); + methodMod.end(); + + MethodOutput methodAnd = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "and", + "(" + signature(IAny.class) + ")" + anySignature, + null, + null); + methodAnd.start(); + compileAnyBinary( + anySignature, + OperatorType.AND, + new EnvironmentMethod(methodAnd, environment)); + methodAnd.end(); + + MethodOutput methodOr = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "or", + "(" + signature(IAny.class) + ")" + anySignature, + null, + null); + methodOr.start(); + compileAnyBinary( + anySignature, + OperatorType.OR, + new EnvironmentMethod(methodOr, environment)); + methodOr.end(); + + MethodOutput methodXor = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "xor", + "(" + signature(IAny.class) + ")" + anySignature, + null, + null); + methodXor.start(); + compileAnyBinary( + anySignature, + OperatorType.XOR, + new EnvironmentMethod(methodXor, environment)); + methodXor.end(); + + MethodOutput methodRange = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "range", + "(" + signature(IAny.class) + ")" + anySignature, + null, + null); + methodRange.start(); + compileAnyBinary( + anySignature, + OperatorType.RANGE, + new EnvironmentMethod(methodRange, environment)); + methodRange.end(); + + MethodOutput methodContains = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "contains", + "(" + signature(IAny.class) + ")" + anySignature, + null, + null); + methodContains.start(); + compileAnyBinary( + anySignature, + OperatorType.CONTAINS, + new EnvironmentMethod(methodContains, environment)); + methodContains.end(); + + MethodOutput methodIndexSet = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "indexSet", + "(" + signature(IAny.class) + ")" + anySignature, + null, + null); + methodIndexSet.start(); + compileAnyBinary( + anySignature, + OperatorType.INDEXSET, + new EnvironmentMethod(methodIndexSet, environment)); + methodIndexSet.end(); + + MethodOutput methodCompare = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "compareTo", + "(" + signature(IAny.class) + ")" + anySignature, + null, + null); + methodCompare.start(); + compileAnyBinary( + anySignature, + OperatorType.COMPARE, + new EnvironmentMethod(methodCompare, environment)); + methodCompare.end(); + + MethodOutput methodMember = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "method", + "(" + signature(String.class) + ")" + anySignature, + null, + null); + methodMember.start(); + compileAnyMember(anySignature, new EnvironmentMethod(methodMember, environment)); + methodMember.end(); + + MethodOutput methodCall = new MethodOutput( + newClass, + Opcodes.ACC_PUBLIC, + "call", + "([" + signature(IAny.class) + ")" + anySignature, + null, + null); + methodCall.start(); + // TODO: complete + methodCall.aConstNull(); + methodCall.returnObject(); + methodCall.end(); + + // TODO: asXXX, iterators + + environment.putClass(anyClassName, newClass.toByteArray()); + } + return; + } else if (toType instanceof ZenTypeNative) { + Class toCls = toType.toJavaClass(); + if (toCls.isAssignableFrom(cls)) { + // nothing to do + return; + } + } + + MethodOutput output = environment.getOutput(); + for (ZenNativeCaster caster : casters) { + if (caster.getCasterClass() == toType.toJavaClass()) { + caster.compile(output); + return; + } + } + for (ZenNativeCaster caster : casters) { + if (toType.toJavaClass().isAssignableFrom(caster.getCasterClass())) { + caster.compile(output); + return; + } + } + if (compileCastExpansion(position, environment, toType)) { + return; + } + + output.checkCast(toType.toJavaClass()); + } + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + for (ZenNativeOperator unaryOperator : unaryOperators) { + if (unaryOperator.getOperator() == operator) { + return unaryOperator + .getMethod() + .callVirtual(position, environment, value); + } + } + + environment.error(position, "operator not supported"); + return new ExpressionInvalid(position); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + for (ZenNativeOperator binaryOperator : binaryOperators) { + if (binaryOperator.getOperator() == operator) { + return binaryOperator + .getMethod() + .callVirtual(position, environment, left, right); + } + } + + environment.error(position, "operator not supported"); + return new ExpressionInvalid(position); + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + for (ZenNativeOperator trinaryOperator : trinaryOperators) { + if (trinaryOperator.getOperator() == operator) { + return trinaryOperator + .getMethod() + .callVirtual(position, environment, first, second, third); + } + } + + environment.error(position, "operator not supported"); + return new ExpressionInvalid(position); + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + if (type == CompareType.EQ || type == CompareType.NE) { + for (ZenNativeOperator binaryOperator : binaryOperators) { + if (binaryOperator.getOperator() == OperatorType.EQUALS) { + Expression result = binaryOperator + .getMethod() + .callVirtual(position, environment, left, right); + if (type == CompareType.EQ) { + return result; + } else { + return new ExpressionArithmeticUnary(position, OperatorType.NOT, result); + } + } + } + } + + return new ExpressionCompareGeneric(position, binary(position, environment, left, right, OperatorType.COMPARE), type); + } + + @Override + public Expression call( + ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + private void compileAnyUnary(String anySignature, OperatorType operator, IEnvironmentMethod environment) { + List operators = new ArrayList(); + for (ZenNativeOperator unary : this.unaryOperators) { + if (unary.getOperator() == operator) { + operators.add(unary); + } + } + + MethodOutput output = environment.getOutput(); + if (operators.isEmpty()) { + output.newObject(ZenRuntimeException.class); + output.dup(); + output.constant("Cannot " + operator + " " + classPkg + '.' + className); + output.construct(ZenRuntimeException.class, String.class); + output.aThrow(); + } else { + if (operators.size() > 1) { + environment.error(null, "Multiple " + operator + " operators for " + cls); + } + + ZenNativeOperator unary = operators.get(0); + + output.loadObject(0); + output.getField(anySignature, "value", signature(cls)); + + output.invoke(unary.getMethod()); + environment.getType(unary.getClass()).compileCast(null, environment, ANY); + output.returnObject(); + } + } + + private void compileAnyBinary(String anySignature, OperatorType operator, IEnvironmentMethod environment) { + List operators = new ArrayList(); + for (ZenNativeOperator binary : binaryOperators) { + if (binary.getOperator() == operator) { + operators.add(binary); + } + } + + MethodOutput output = environment.getOutput(); + if (operators.isEmpty()) { + output.newObject(ZenRuntimeException.class); + output.dup(); + output.constant("Cannot " + operator + " on " + classPkg + '.' + className); + output.construct(ZenRuntimeException.class, String.class); + output.aThrow(); + } else if (operators.size() == 1) { + ZenNativeOperator binary = operators.get(0); + + output.loadObject(0); + output.getField(anySignature, "value", signature(cls)); + output.loadObject(1); + output.invoke(binary.getMethod()); + environment.getType(binary.getClass()).compileCast(null, environment, ANY); + output.returnObject(); + } else { + environment.error(null, "Multiple " + operator + " operators for " + cls + " (which is not yet supported)"); + } + } + + public void compileAnyMember(String anySignature, EnvironmentMethod environment) { + // TODO: complete + MethodOutput output = environment.getOutput(); + output.aConstNull(); + output.returnObject(); + } + + @Override + public String getName() { + return classPkg + '.' + className; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionNull(position); + } + + private void addSubtypes(Queue todo, ITypeRegistry types) { + while (!todo.isEmpty()) { + ZenTypeNative current = todo.poll(); + if (current.cls.getSuperclass() != Object.class) { + ZenType type = types.getType(current.cls.getSuperclass()); + if (type instanceof ZenTypeNative) { + todo.offer((ZenTypeNative) type); + implementing.add((ZenTypeNative) type); + } + } + for (Class iface : current.cls.getInterfaces()) { + ZenType type = types.getType(iface); + if (type instanceof ZenTypeNative) { + todo.offer((ZenTypeNative) type); + implementing.add((ZenTypeNative) type); + } + } + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeNull.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeNull.java new file mode 100644 index 0000000..acdf81a --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeNull.java @@ -0,0 +1,134 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.ExpressionNull; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ZenTypeNull extends ZenType { + public static final ZenTypeNull INSTANCE = new ZenTypeNull(); + private static final Type TYPE = Type.getType(Object.class); + + private ZenTypeNull() { + + } + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + environment.error(position, "null has not operators"); + return new ExpressionInvalid(position); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + environment.error(position, "null has not operators"); + return new ExpressionInvalid(position); + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + environment.error(position, "null has not operators"); + return new ExpressionInvalid(position); + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + environment.error(position, "null has not operators"); + return new ExpressionInvalid(position); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + environment.error(position, "null doesn't have members"); + return new ExpressionInvalid(position); + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + environment.error(position, "null doesn't have static members"); + return new ExpressionInvalid(position); + } + + @Override + public Expression call( + ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + environment.error(position, "cannot call null values"); + return new ExpressionInvalid(position); + } + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod environment) { + return null; + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return type.isPointer(); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return type.isPointer(); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + return value; + } + + @Override + public Class toJavaClass() { + return Object.class; + } + + @Override + public Type toASMType() { + return TYPE; + } + + @Override + public int getNumberType() { + return 0; + } + + @Override + public String getSignature() { + return "Ljava/lang/Object;"; + } + + @Override + public boolean isPointer() { + return true; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + // nothing to do + } + + @Override + public String getName() { + return "null"; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionNull(position); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeShort.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeShort.java new file mode 100644 index 0000000..44f6ad4 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeShort.java @@ -0,0 +1,178 @@ +package stanhebben.zenscript.type; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionArithmeticBinary; +import stanhebben.zenscript.expression.ExpressionArithmeticCompare; +import stanhebben.zenscript.expression.ExpressionArithmeticUnary; +import stanhebben.zenscript.expression.ExpressionAs; +import stanhebben.zenscript.expression.ExpressionInt; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +public class ZenTypeShort extends ZenType { + public static final ZenTypeShort INSTANCE = new ZenTypeShort(); + + private ZenTypeShort() {} + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod environment) { + return null; + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return (type.getNumberType() != 0 && type.getNumberType() <= NUM_SHORT) + || type == ZenTypeString.INSTANCE + || canCastExpansion(environment, type); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return type.getNumberType() != 0 + || type == ZenTypeString.INSTANCE + || canCastExpansion(environment, type); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type.getNumberType() > 0 || type == STRING) { + return new ExpressionAs(position, value, type); + } else if (canCastExpansion(environment, type)) { + return castExpansion(position, environment, value, type); + } else { + return new ExpressionAs(position, value, type); + } + } + + @Override + public Type toASMType() { + return Type.SHORT_TYPE; + } + + @Override + public int getNumberType() { + return NUM_SHORT; + } + + @Override + public IPartialExpression getMember( + ZenPosition position, + IEnvironmentGlobal environment, + IPartialExpression value, + String name) { + IPartialExpression result = memberExpansion(position, environment, value.eval(environment), name); + if (result == null) { + environment.error(position, "bool value has no members"); + return new ExpressionInvalid(position, ZenTypeAny.INSTANCE); + } else { + return result; + } + } + + @Override + public IPartialExpression getStaticMember( + ZenPosition position, + IEnvironmentGlobal environment, + String name) { + return null; + } + + @Override + public String getSignature() { + return "S"; + } + + @Override + public boolean isPointer() { + return false; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + MethodOutput output = environment.getOutput(); + + if (type == BYTE) { + output.i2b(); + } else if (type == ZenTypeByteObject.INSTANCE) { + output.i2b(); + output.invokeStatic(Byte.class, "valueOf", Byte.class, byte.class); + } else if (type == SHORT) { + // nothing to do + } else if (type == ZenTypeShortObject.INSTANCE) { + output.invokeStatic(Short.class, "valueOf", Short.class, short.class); + } else if (type == INT) { + // nothing to do + } else if (type == ZenTypeIntObject.INSTANCE) { + output.invokeStatic(Integer.class, "valueOf", Integer.class, int.class); + } else if (type == LONG) { + output.i2l(); + } else if (type == ZenTypeLongObject.INSTANCE) { + output.i2l(); + output.invokeStatic(Long.class, "valueOf", Long.class, long.class); + } else if (type == FLOAT) { + output.i2f(); + } else if (type == ZenTypeFloatObject.INSTANCE) { + output.i2f(); + output.invokeStatic(Float.class, "valueOf", Float.class, float.class); + } else if (type == DOUBLE) { + output.i2d(); + } else if (type == ZenTypeDoubleObject.INSTANCE) { + output.i2d(); + output.invokeStatic(Double.class, "valueOf", Double.class, double.class); + } else if (type == STRING) { + output.invokeStatic(Short.class, "toString", String.class, short.class); + } else if (!compileCastExpansion(position, environment, type)) { + environment.error(position, "cannot cast " + this + " to " + type); + } + } + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + return new ExpressionArithmeticUnary(position, operator, value); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + return new ExpressionArithmeticBinary(position, operator, left, right.cast(position, environment, this)); + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + environment.error(position, "short doesn't support this operation"); + return new ExpressionInvalid(position); + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + return new ExpressionArithmeticCompare(position, type, left, right.cast(position, environment, this)); + } + + @Override + public Expression call( + ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + environment.error(position, "cannot call short values"); + return new ExpressionInvalid(position); + } + + @Override + public Class toJavaClass() { + return short.class; + } + + @Override + public String getName() { + return "short"; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionInt(position, 0, SHORT); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeShortObject.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeShortObject.java new file mode 100644 index 0000000..260611d --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeShortObject.java @@ -0,0 +1,140 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionAs; +import stanhebben.zenscript.expression.ExpressionNull; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import static stanhebben.zenscript.type.ZenType.STRING; +import stanhebben.zenscript.util.ZenPosition; +import static stanhebben.zenscript.util.ZenTypeUtil.signature; + +/** + * + * @author Stan + */ +public class ZenTypeShortObject extends ZenType { + public static final ZenTypeShortObject INSTANCE = new ZenTypeShortObject(); + + private ZenTypeShortObject() {} + + @Override + public Expression unary(ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + return SHORT.unary(position, environment, value.cast(position, environment, SHORT), operator); + } + + @Override + public Expression binary(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + return SHORT.binary(position, environment, left.cast(position, environment, SHORT), right, operator); + } + + @Override + public Expression trinary(ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + return SHORT.trinary(position, environment, first.cast(position, environment, SHORT), second, third, operator); + } + + @Override + public Expression compare(ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + return SHORT.compare(position, environment, left.cast(position, environment, SHORT), right, type); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value, String name) { + return SHORT.getMember(position, environment, value.eval(environment).cast(position, environment, SHORT), name); + } + + @Override + public IPartialExpression getStaticMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + return SHORT.getStaticMember(position, environment, name); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + return SHORT.call(position, environment, receiver.cast(position, environment, SHORT), arguments); + } + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod environment) { + return SHORT.makeIterator(numValues, environment); + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return SHORT.canCastImplicit(type, environment); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return SHORT.canCastExplicit(type, environment); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type.getNumberType() > 0 || type == STRING) { + return new ExpressionAs(position, value, type); + } else if (canCastExpansion(environment, type)) { + return castExpansion(position, environment, value, type); + } else { + return new ExpressionAs(position, value, type); + } + } + + @Override + public Class toJavaClass() { + return Short.class; + } + + @Override + public Type toASMType() { + return Type.getType(Short.class); + } + + @Override + public int getNumberType() { + return NUM_SHORT; + } + + @Override + public String getSignature() { + return signature(Short.class); + } + + @Override + public boolean isPointer() { + return true; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + if (type == this) { + // nothing to do + } else if (type == SHORT) { + environment.getOutput().invokeVirtual(Short.class, "shortValue", short.class); + } else if (type == STRING) { + environment.getOutput().invokeVirtual(Short.class, "toString", String.class); + } else { + environment.getOutput().invokeVirtual(Short.class, "shortValue", short.class); + SHORT.compileCast(position, environment, type); + } + } + + @Override + public String getName() { + return "short"; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionNull(position); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeString.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeString.java new file mode 100644 index 0000000..39003fc --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeString.java @@ -0,0 +1,216 @@ +package stanhebben.zenscript.type; + +import java.util.ArrayList; +import java.util.List; +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionAs; +import stanhebben.zenscript.expression.ExpressionCompareGeneric; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.ExpressionNull; +import stanhebben.zenscript.expression.ExpressionStringConcat; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.MethodOutput; +import stanhebben.zenscript.util.ZenPosition; + +public class ZenTypeString extends ZenType { + public static final ZenTypeString INSTANCE = new ZenTypeString(); + + private final Type type = Type.getType("java.lang.String"); + + private ZenTypeString() {} + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + return null; + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return type == this || type == BOOL || type.getNumberType() > 0 || canCastExpansion(environment, type); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return type == this || type == BOOL || type.getNumberType() > 0 || canCastExpansion(environment, type); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + if (type == this) { + return value; + } else if (type == BOOL || type.getNumberType() > 0) { + return new ExpressionAs(position, value, type); + } else if (canCastExpansion(environment, type)) { + return castExpansion(position, environment, value, type); + } else { + return new ExpressionAs(position, value, type); + } + } + + @Override + public Type toASMType() { + return type; + } + + @Override + public int getNumberType() { + return 0; + } + + @Override + public IPartialExpression getMember( + ZenPosition position, + IEnvironmentGlobal environment, + IPartialExpression value, + String name) { + IPartialExpression result = memberExpansion(position, environment, value.eval(environment), name); + if (result == null) { + environment.error(position, "bool value has no members"); + return new ExpressionInvalid(position, ZenTypeAny.INSTANCE); + } else { + return result; + } + } + + @Override + public IPartialExpression getStaticMember( + ZenPosition position, + IEnvironmentGlobal environment, + String name) { + return null; + } + + @Override + public String getSignature() { + return "Ljava/lang/String;"; + } + + @Override + public boolean isPointer() { + return true; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + MethodOutput output = environment.getOutput(); + + if (type == BOOL) { + output.invokeStatic(Boolean.class, "parseBoolean", boolean.class, String.class); + } else if (type == ZenTypeBoolObject.INSTANCE) { + output.invokeStatic(Boolean.class, "valueOf", Boolean.class, String.class); + } else if (type == BYTE) { + output.invokeStatic(Byte.class, "parseByte", byte.class, String.class); + } else if (type == ZenTypeByteObject.INSTANCE) { + output.invokeStatic(Byte.class, "valueOf", Byte.class, String.class); + } else if (type == SHORT) { + output.invokeStatic(Short.class, "parseShort", short.class, String.class); + } else if (type == ZenTypeShortObject.INSTANCE) { + output.invokeStatic(Short.class, "valueof", Short.class, String.class); + } else if (type == INT) { + output.invokeStatic(Integer.class, "parseInt", int.class, String.class); + } else if (type == ZenTypeIntObject.INSTANCE) { + output.invokeStatic(Integer.class, "valueof", Integer.class, String.class); + } else if (type == LONG) { + output.invokeStatic(Long.class, "parseLong", long.class, String.class); + } else if (type == ZenTypeLongObject.INSTANCE) { + output.invokeStatic(Long.class, "valueof", Long.class, String.class); + } else if (type == FLOAT) { + output.invokeStatic(Float.class, "parseFloat", float.class, String.class); + } else if (type == ZenTypeFloatObject.INSTANCE) { + output.invokeStatic(Float.class, "valueOf", Float.class, String.class); + } else if (type == DOUBLE) { + output.invokeStatic(Double.class, "parseDouble", double.class, String.class); + } else if (type == ZenTypeDoubleObject.INSTANCE) { + output.invokeStatic(Double.class, "valueOf", Double.class, String.class); + } else if (!compileCastExpansion(position, environment, type)) { + environment.error(position, "cannot cast " + this + " to " + type); + } + } + + @Override + public Expression unary( + ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + Expression result = unaryExpansion(position, environment, value, operator); + if (result == null) { + environment.error(position, "operator not supported on a string"); + return new ExpressionInvalid(position); + } else { + return result; + } + } + + @Override + public Expression binary( + ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + if (operator == OperatorType.CAT || operator == OperatorType.ADD) { + if (left instanceof ExpressionStringConcat) { + ((ExpressionStringConcat) left).add(right.cast(position, environment, this)); + return left; + } else { + List values = new ArrayList(); + values.add(left); + values.add(right.cast(position, environment, this)); + return new ExpressionStringConcat(position, values); + } + } else { + Expression result = binaryExpansion(position, environment, left, right, operator); + if (result == null) { + environment.error(position, "operator not supported on strings"); + return new ExpressionInvalid(position, this); + } else { + return result; + } + } + } + + @Override + public Expression trinary( + ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + Expression result = trinaryExpansion(position, environment, first, second, third, operator); + if (result == null) { + environment.error(position, "operator not supported on strings"); + return new ExpressionInvalid(position, this); + } else { + return result; + } + } + + @Override + public Expression compare( + ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + Expression result = binaryExpansion(position, environment, left, right, OperatorType.COMPARE); + if (result == null) { + environment.error(position, "cannot compare strings"); + return new ExpressionInvalid(position, BOOL); + } else { + return new ExpressionCompareGeneric(position, result, type); + } + } + + @Override + public Expression call( + ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + environment.error(position, "Cannot call a string value"); + return new ExpressionInvalid(position, INSTANCE); + } + + @Override + public Class toJavaClass() { + return String.class; + } + + @Override + public String getName() { + return "string"; + } + + @Override + public Expression defaultValue(ZenPosition position) { + return new ExpressionNull(position); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeVoid.java b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeVoid.java new file mode 100644 index 0000000..8e1df69 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/ZenTypeVoid.java @@ -0,0 +1,148 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type; + +import org.objectweb.asm.Type; +import stanhebben.zenscript.annotations.CompareType; +import stanhebben.zenscript.annotations.OperatorType; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ZenTypeVoid extends ZenType { + public static final ZenTypeVoid INSTANCE = new ZenTypeVoid(); + + private ZenTypeVoid() {} + + @Override + public IPartialExpression getMember( + ZenPosition position, + IEnvironmentGlobal environment, + IPartialExpression value, + String name) { + environment.error(position, "void doesn't have members"); + return new ExpressionInvalid(position, this); + } + + @Override + public IPartialExpression getStaticMember( + ZenPosition position, + IEnvironmentGlobal environment, + String name) { + environment.error(position, "void doesn't have static members"); + return new ExpressionInvalid(position, this); + } + + @Override + public IZenIterator makeIterator(int numValues, IEnvironmentMethod methodOutput) { + return null; + } + + @Override + public boolean canCastImplicit(ZenType type, IEnvironmentGlobal environment) { + return type == this || canCastExpansion(environment, type); + } + + @Override + public boolean canCastExplicit(ZenType type, IEnvironmentGlobal environment) { + return type == this || canCastExpansion(environment, type); + } + + @Override + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression value, ZenType type) { + environment.error(position, "cannot cast void to other type"); + return new ExpressionInvalid(position, type); + } + + @Override + public Type toASMType() { + return Type.VOID_TYPE; + } + + @Override + public int getNumberType() { + return 0; + } + + @Override + public String getSignature() { + return "V"; + } + + @Override + public boolean isPointer() { + return false; + } + + @Override + public void compileCast(ZenPosition position, IEnvironmentMethod environment, ZenType type) { + if (type == this) { + // nothing to do + } else { + if (!compileCastExpansion(position, environment, type)) { + environment.error(position, "cannot cast " + this + " to " + type); + } + } + } + + @Override + public Expression unary( + ZenPosition position, IEnvironmentGlobal environment, Expression value, OperatorType operator) { + environment.error(position, "void does not have operators"); + return new ExpressionInvalid(position, this); + } + + @Override + public Expression binary( + ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, OperatorType operator) { + environment.error(position, "void does not have operators"); + return new ExpressionInvalid(position, this); + } + + @Override + public Expression trinary( + ZenPosition position, IEnvironmentGlobal environment, Expression first, Expression second, Expression third, OperatorType operator) { + environment.error(position, "void does not have operators"); + return new ExpressionInvalid(position, this); + } + + @Override + public Expression compare( + ZenPosition position, IEnvironmentGlobal environment, Expression left, Expression right, CompareType type) { + environment.error(position, "void does not have operators"); + return new ExpressionInvalid(position, this); + } + + @Override + public Expression call( + ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + environment.error(position, "cannot call a void"); + return new ExpressionInvalid(position, this); + } + + @Override + public Class toJavaClass() { + return void.class; + } + + @Override + public String getName() { + return "void"; + } + + @Override + public Expression defaultValue(ZenPosition position) { + throw new RuntimeException("void has no default value"); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/expand/ZenExpandCaster.java b/ZenScript/src/main/java/stanhebben/zenscript/type/expand/ZenExpandCaster.java new file mode 100644 index 0000000..146ac2e --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/expand/ZenExpandCaster.java @@ -0,0 +1,38 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type.expand; + +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stanneke + */ +public class ZenExpandCaster { + private final JavaMethod method; + + public ZenExpandCaster(JavaMethod method) { + this.method = method; + } + + public ZenType getTarget() { + return method.getReturnType(); + } + + public Expression cast(ZenPosition position, IEnvironmentGlobal environment, Expression expression) { + return method.callStatic(position, environment, expression); + } + + public void compile(IEnvironmentMethod environment) { + environment.getOutput().invokeStatic(method); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/expand/ZenExpandMember.java b/ZenScript/src/main/java/stanhebben/zenscript/type/expand/ZenExpandMember.java new file mode 100644 index 0000000..aa7ddb5 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/expand/ZenExpandMember.java @@ -0,0 +1,161 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type.expand; + +import java.util.ArrayList; +import java.util.List; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.natives.JavaMethod; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +public class ZenExpandMember { + private JavaMethod getter; + private JavaMethod setter; + private final List methods = new ArrayList(); + + public IPartialExpression instance(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value) { + return new InstanceGetValue(position, value); + } + + public IPartialExpression instance(ZenPosition position, IEnvironmentGlobal environment) { + return new StaticGetValue(position); + } + + public void setGetter(JavaMethod getter) { + if (getter != null) { + throw new RuntimeException("already have a getter"); + } else { + this.getter = getter; + } + } + + public void setSetter(JavaMethod setter) { + if (setter != null) { + throw new RuntimeException("already have a setter"); + } else { + this.setter = setter; + } + } + + public void addMethod(JavaMethod method) { + methods.add(method); + } + + private class InstanceGetValue implements IPartialExpression { + private final ZenPosition position; + private final IPartialExpression value; + + public InstanceGetValue(ZenPosition position, IPartialExpression value) { + this.position = position; + this.value = value; + } + + @Override + public Expression eval(IEnvironmentGlobal environment) { + return getter.callVirtual(position, environment, value.eval(environment)); + } + + @Override + public Expression assign(ZenPosition position, IEnvironmentGlobal environment, Expression other) { + return setter.callVirtual(position, environment, value.eval(environment), other); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + ZenType type = environment.getType(getter.getMethod().getGenericReturnType()); + return type.getMember(position, environment, this, name); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentMethod environment, Expression... values) { + Expression[] newValues = new Expression[values.length + 1]; + newValues[0] = value.eval(environment); + System.arraycopy(values, 0, newValues, 1, values.length); + JavaMethod method = JavaMethod.select(true, methods, environment, newValues); + if (method == null) { + environment.error(position, "no matching method found"); + return new ExpressionInvalid(position); + } else { + return method.callStatic(position, environment, newValues); + } + } + + @Override + public IZenSymbol toSymbol() { + return null; + } + + @Override + public ZenType toType(IEnvironmentGlobal environment) { + environment.error(position, "not a valid type"); + return ZenType.ANY; + } + } + + private class StaticGetValue implements IPartialExpression { + private final ZenPosition position; + + public StaticGetValue(ZenPosition position) { + this.position = position; + } + + @Override + public Expression eval(IEnvironmentGlobal environment) { + return setter.callStatic(position, environment); + } + + @Override + public Expression assign(ZenPosition position, IEnvironmentGlobal environment, Expression other) { + return setter.callStatic(position, environment, other); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + ZenType type = environment.getType(getter.getMethod().getGenericReturnType()); + return type.getMember(position, environment, this, name); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentMethod environment, Expression... values) { + JavaMethod method = JavaMethod.select(true, methods, environment, values); + if (method == null) { + environment.error(position, "no matching method found"); + return new ExpressionInvalid(position); + } else { + return method.callStatic(position, environment, values); + } + } + + @Override + public IZenSymbol toSymbol() { + return new StaticSymbol(); + } + + @Override + public ZenType toType(IEnvironmentGlobal environment) { + environment.error(position, "not a valid type"); + return ZenType.ANY; + } + } + + private class StaticSymbol implements IZenSymbol { + @Override + public IPartialExpression instance(ZenPosition position) { + return new StaticGetValue(position); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/iterator/IteratorIterable.java b/ZenScript/src/main/java/stanhebben/zenscript/type/iterator/IteratorIterable.java new file mode 100644 index 0000000..212567b --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/iterator/IteratorIterable.java @@ -0,0 +1,66 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type.iterator; + +import java.util.Iterator; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import stanhebben.zenscript.type.IZenIterator; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.MethodOutput; + +/** + * + * @author Stan + */ +public class IteratorIterable implements IZenIterator { + private final MethodOutput methodOutput; + private final ZenType iteratorType; + private int iterator; + + public IteratorIterable(MethodOutput methodOutput, ZenType iteratorType) { + this.methodOutput = methodOutput; + this.iteratorType = iteratorType; + } + + @Override + public void compileStart(int[] locals) { + iterator = methodOutput.local(Type.getType(Iterator.class)); + methodOutput.invokeVirtual(Iterator.class, "iterator", Iterator.class); + methodOutput.storeObject(iterator); + } + + @Override + public void compilePreIterate(int[] locals, Label exit) { + methodOutput.dup(); + methodOutput.invokeInterface( + Iterator.class, + "hasNext", + boolean.class); + methodOutput.ifEQ(exit); + + methodOutput.dup(); + methodOutput.invokeInterface(Iterator.class, "next", Object.class); + methodOutput.store(iteratorType.toASMType(), locals[0]); + } + + @Override + public void compilePostIterate(int[] locals, Label exit, Label repeat) { + methodOutput.goTo(repeat); + } + + @Override + public ZenType getType(int i) { + return iteratorType; + } + + @Override + public void compileEnd() { + // pop iterator + methodOutput.pop(); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/iterator/IteratorList.java b/ZenScript/src/main/java/stanhebben/zenscript/type/iterator/IteratorList.java new file mode 100644 index 0000000..97f46cc --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/iterator/IteratorList.java @@ -0,0 +1,70 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type.iterator; + +import java.util.Iterator; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import stanhebben.zenscript.type.IZenIterator; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.MethodOutput; + +/** + * + * @author Stan + */ + + +public class IteratorList implements IZenIterator { + private final MethodOutput methodOutput; + private final ZenType iteratorType; + private int iterator; + + public IteratorList(MethodOutput methodOutput, ZenType iteratorType) { + this.methodOutput = methodOutput; + this.iteratorType = iteratorType; + } + + @Override + public void compileStart(int[] locals) { + iterator = methodOutput.local(Type.getType(Iterator.class)); + methodOutput.invokeInterface(Iterable.class, "iterator", Iterator.class); + methodOutput.storeObject(iterator); + methodOutput.iConst0(); + methodOutput.storeInt(locals[0]); + } + + @Override + public void compilePreIterate(int[] locals, Label exit) { + methodOutput.dup(); + methodOutput.invokeInterface( + Iterator.class, + "hasNext", + boolean.class); + methodOutput.ifEQ(exit); + + methodOutput.dup(); + methodOutput.invokeInterface(Iterator.class, "next", Object.class); + methodOutput.store(iteratorType.toASMType(), locals[1]); + } + + @Override + public void compilePostIterate(int[] locals, Label exit, Label repeat) { + methodOutput.iinc(locals[0]); + methodOutput.goTo(repeat); + } + + @Override + public void compileEnd() { + methodOutput.pop(); + } + + @Override + public ZenType getType(int i) { + return i == 0 ? ZenType.INT : iteratorType; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/iterator/IteratorMap.java b/ZenScript/src/main/java/stanhebben/zenscript/type/iterator/IteratorMap.java new file mode 100644 index 0000000..a6ab16d --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/iterator/IteratorMap.java @@ -0,0 +1,75 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type.iterator; + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import stanhebben.zenscript.type.IZenIterator; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeAssociative; +import stanhebben.zenscript.util.MethodOutput; + +/** + * + * @author Stan + */ +public class IteratorMap implements IZenIterator { + private final MethodOutput methodOutput; + private final ZenTypeAssociative type; + private int iterator; + + public IteratorMap(MethodOutput methodOutput, ZenTypeAssociative type) { + this.methodOutput = methodOutput; + this.type = type; + } + + @Override + public void compileStart(int[] locals) { + methodOutput.invokeInterface(Map.class, "entrySet", Set.class); + + iterator = methodOutput.local(Type.getType(Iterator.class)); + methodOutput.invokeInterface(Set.class, "iterator", Iterator.class); + methodOutput.storeObject(iterator); + } + + @Override + public void compilePreIterate(int[] locals, Label exit) { + methodOutput.dup(); + methodOutput.invokeInterface( + Iterator.class, + "hasNext", + boolean.class); + methodOutput.ifEQ(exit); + + methodOutput.invokeInterface(Iterator.class, "next", Object.class); + methodOutput.dup(); + + methodOutput.invokeInterface(Map.Entry.class, "getKey", Object.class); + methodOutput.store(type.getKeyType().toASMType(), locals[0]); + + methodOutput.invokeInterface(Map.Entry.class, "getValue", Object.class); + methodOutput.store(type.getValueType().toASMType(), locals[1]); + } + + @Override + public void compilePostIterate(int[] locals, Label exit, Label repeat) { + methodOutput.goTo(repeat); + } + + @Override + public void compileEnd() { + methodOutput.pop(); + } + + @Override + public ZenType getType(int i) { + return i == 1 ? type.getValueType() : type.getKeyType(); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/iterator/IteratorMapKeys.java b/ZenScript/src/main/java/stanhebben/zenscript/type/iterator/IteratorMapKeys.java new file mode 100644 index 0000000..68630f9 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/iterator/IteratorMapKeys.java @@ -0,0 +1,71 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type.iterator; + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import stanhebben.zenscript.type.IZenIterator; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeAssociative; +import stanhebben.zenscript.util.MethodOutput; + +/** + * + * @author Stan + */ +public class IteratorMapKeys implements IZenIterator { + private final MethodOutput methodOutput; + private final ZenTypeAssociative type; + private int iterator; + + public IteratorMapKeys(MethodOutput methodOutput, ZenTypeAssociative type) { + this.methodOutput = methodOutput; + this.type = type; + } + + @Override + public void compileStart(int[] locals) { + methodOutput.invokeInterface(Map.class, "entrySet", Set.class); + + iterator = methodOutput.local(Type.getType(Iterator.class)); + methodOutput.invokeInterface(Set.class, "iterator", Iterator.class); + methodOutput.storeObject(iterator); + } + + @Override + public void compilePreIterate(int[] locals, Label exit) { + methodOutput.dup(); + methodOutput.invokeInterface( + Iterator.class, + "hasNext", + boolean.class); + methodOutput.ifEQ(exit); + + methodOutput.invokeInterface(Iterator.class, "next", Object.class); + + methodOutput.invokeInterface(Map.Entry.class, "getKey", Object.class); + methodOutput.store(type.getKeyType().toASMType(), locals[0]); + } + + @Override + public void compilePostIterate(int[] locals, Label exit, Label repeat) { + methodOutput.goTo(repeat); + } + + @Override + public void compileEnd() { + methodOutput.pop(); + } + + @Override + public ZenType getType(int i) { + return type.getKeyType(); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/natives/JavaMethod.java b/ZenScript/src/main/java/stanhebben/zenscript/type/natives/JavaMethod.java new file mode 100644 index 0000000..c32df68 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/natives/JavaMethod.java @@ -0,0 +1,310 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type.natives; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.List; +import stanhebben.zenscript.annotations.Optional; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.ITypeRegistry; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionArray; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.ExpressionJavaCallStatic; +import stanhebben.zenscript.expression.ExpressionJavaCallVirtual; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.type.ZenTypeArray; +import stanhebben.zenscript.type.ZenTypeArrayBasic; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +public class JavaMethod { + public static final int PRIORITY_INVALID = -1; + public static final int PRIORITY_LOW = 1; + public static final int PRIORITY_MEDIUM = 2; + public static final int PRIORITY_HIGH = 3; + + public static JavaMethod get(ITypeRegistry types, Class cls, String name, Class... parameterTypes) { + try { + Method method = cls.getMethod(name, parameterTypes); + return new JavaMethod(cls, method, types); + } catch (NoSuchMethodException ex) { + throw new RuntimeException("method not found", ex); + } catch (SecurityException ex) { + throw new RuntimeException("method retrieval not permitted", ex); + } + } + + public static JavaMethod select(boolean doStatic, List methods, IEnvironmentGlobal environment, Expression... arguments) { + int bestPriority = PRIORITY_INVALID; + JavaMethod bestMethod = null; + boolean isValid = false; + + for (JavaMethod method : methods) { + if (method.isStatic() != doStatic) continue; + + int priority = method.getPriority(environment, arguments); + if (priority == bestPriority) { + isValid = false; + } else if (priority > bestPriority) { + isValid = true; + bestMethod = method; + bestPriority = priority; + } + } + + return isValid ? bestMethod : null; + } + + public static ZenType[] predict(List methods, int numArguments, IEnvironmentGlobal environment) { + ZenType[] results = new ZenType[numArguments]; + boolean[] ambiguous = new boolean[numArguments]; + + for (JavaMethod method : methods) { + if (method.accepts(numArguments)) { + for (int i = 0; i < numArguments; i++) { + if (i >= method.parameterTypes.length - 1 && method.method.isVarArgs()) { + if (numArguments == method.parameterTypes.length) { + ambiguous[i] = true; + } else if (numArguments > method.parameterTypes.length) { + if (results[i] != null) { + ambiguous[i] = true; + } else { + results[i] = ((ZenTypeArray) method.parameterTypes[method.parameterTypes.length - 1]).getBaseType(); + } + } + } + + if (results[i] != null) { + ambiguous[i] = true; + } else { + results[i] = method.parameterTypes[i]; + } + } + } + } + + for (int i = 0; i < results.length; i++) { + if (ambiguous[i]) { + results[i] = null; + } + } + + return results; + } + + private final Class owner; + private final Method method; + + private final ZenType[] parameterTypes; + private final boolean[] optional; + private final ZenType returnType; + + public JavaMethod(Class owner, Method method, ITypeRegistry types) { + this.owner = owner; + this.method = method; + + returnType = types.getType(method.getGenericReturnType()); + parameterTypes = new ZenType[method.getParameterCount()]; + optional = new boolean[parameterTypes.length]; + for (int i = 0; i < parameterTypes.length; i++) { + parameterTypes[i] = types.getType(method.getGenericParameterTypes()[i]); + + optional[i] = false; + for (Annotation annotation : method.getParameterAnnotations()[i]) { + if (annotation instanceof Optional) { + optional[i] = true; + } + } + } + + if (method.isVarArgs()) { + optional[parameterTypes.length - 1] = true; + } + } + + public boolean isStatic() { + return (method.getModifiers() & Modifier.STATIC) > 0; + } + + public ZenType getReturnType() { + return returnType; + } + + public ZenType[] getParameterTypes() { + return parameterTypes; + } + + public Class getOwner() { + return owner; + } + + public Method getMethod() { + return method; + } + + public boolean accepts(IEnvironmentGlobal environment, Expression... arguments) { + return getPriority(environment, arguments) > 0; + } + + public boolean accepts(int numArguments) { + if (numArguments > parameterTypes.length) { + return method.isVarArgs(); + } if (numArguments == parameterTypes.length) { + return true; + } else { + for (int i = numArguments; i < parameterTypes.length; i++) { + if (!optional[i]) return false; + } + return true; + } + } + + public int getPriority(IEnvironmentGlobal environment, Expression... arguments) { + int result = PRIORITY_HIGH; + if (arguments.length > parameterTypes.length) { + if (method.isVarArgs()) { + ZenType arrayType = parameterTypes[method.getParameterCount() - 1]; + ZenType baseType = ((ZenTypeArray) arrayType).getBaseType(); + for (int i = parameterTypes.length - 1; i < arguments.length; i++) { + ZenType argType = arguments[i].getType(); + if (argType.equals(baseType)) { + // OK + } else if (argType.canCastImplicit(baseType, environment)) { + result = Math.min(result, PRIORITY_LOW); + } else { + return PRIORITY_INVALID; + } + } + } else { + return PRIORITY_INVALID; + } + } else if (arguments.length < parameterTypes.length) { + result = PRIORITY_MEDIUM; + + int checkUntil = parameterTypes.length; + if (method.isVarArgs()) { + checkUntil--; + } + + checkOptional: for (int i = arguments.length; i < checkUntil; i++) { + if (!optional[i]) { + return PRIORITY_INVALID; + } + } + } + + int checkUntil = arguments.length; + if (arguments.length == parameterTypes.length && method.isVarArgs()) { + ZenType arrayType = parameterTypes[method.getParameterCount() - 1]; + ZenType baseType = ((ZenTypeArray) arrayType).getBaseType(); + ZenType argType = arguments[arguments.length - 1].getType(); + + if (argType.equals(arrayType)) { + // OK + } else if (argType.equals(baseType)) { + // OK + } else if (argType.canCastImplicit(arrayType, environment)) { + result = Math.min(result, PRIORITY_LOW); + } else if (argType.canCastImplicit(baseType, environment)) { + result = Math.min(result, PRIORITY_LOW); + } else { + return PRIORITY_INVALID; + } + + checkUntil = arguments.length - 1; + } + + for (int i = 0; i < checkUntil; i++) { + ZenType argType = arguments[i].getType(); + ZenType paramType = parameterTypes[i]; + if (!argType.equals(paramType)) { + if (argType.canCastImplicit(paramType, environment)) { + result = Math.min(result, PRIORITY_LOW); + } else { + return PRIORITY_INVALID; + } + } + } + + return result; + } + + public Expression callVirtual(ZenPosition position, IEnvironmentGlobal environment, Expression receiver, Expression... arguments) { + if (isStatic()) { + environment.error(position, "trying to call a static method dynamically"); + return new ExpressionInvalid(position, environment.getType(method.getReturnType())); + } else { + Expression[] actual = rematch(position, environment, arguments); + return new ExpressionJavaCallVirtual(position, this, receiver, actual); + } + } + + public Expression callStatic(ZenPosition position, IEnvironmentGlobal environment, Expression... arguments) { + if (isStatic()) { + Expression[] actual = rematch(position, environment, arguments); + return new ExpressionJavaCallStatic(position, this, actual); + } else { + environment.error(position, "trying to call a non-static method statically"); + return new ExpressionInvalid(position, environment.getType(method.getReturnType())); + } + } + + private Expression[] rematch(ZenPosition position, IEnvironmentGlobal environment, Expression... arguments) { + // small optimization - don't run through this all if not necessary + if (arguments.length == 0 && parameterTypes.length == 0) { + return arguments; + } + + Expression[] result = new Expression[method.getParameterCount()]; + for (int i = arguments.length; i < method.getParameterCount(); i++) { + result[i] = parameterTypes[i].defaultValue(position); + } + + int doUntil = parameterTypes.length; + if (method.isVarArgs()) { + doUntil = arguments.length - 1; + ZenType paramType = parameterTypes[parameterTypes.length - 1]; + ZenType baseType = ((ZenTypeArray) paramType).getBaseType(); + + if (arguments.length == parameterTypes.length) { + ZenType argType = arguments[arguments.length - 1].getType(); + + if (argType.equals(paramType)) { + result[arguments.length - 1] = arguments[arguments.length - 1]; + } else if (argType.equals(baseType)) { + result[arguments.length - 1] = new ExpressionArray(position, arguments[arguments.length - 1]); + } else if (argType.canCastImplicit(paramType, environment)) { + result[arguments.length - 1] = arguments[arguments.length - 1].cast(position, environment, paramType); + } else { + result[arguments.length - 1] = new ExpressionArray(position, arguments[arguments.length - 1]).cast(position, environment, paramType); + } + } else if (arguments.length > parameterTypes.length) { + int offset = parameterTypes.length - 1; + Expression[] values = new Expression[arguments.length - offset]; + for (int i = 0; i < values.length; i++) { + values[i] = arguments[offset + i].cast(position, environment, baseType); + } + result[arguments.length - 1] = new ExpressionArray(position, values, (ZenTypeArrayBasic) paramType); + } + } + + for (int i = arguments.length; i < doUntil; i++) { + result[i] = parameterTypes[i].defaultValue(position); + } + for (int i = 0; i < Math.min(arguments.length, doUntil); i++) { + result[i] = arguments[i].cast(position, environment, parameterTypes[i]); + } + + return result; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/natives/ZenNativeCaster.java b/ZenScript/src/main/java/stanhebben/zenscript/type/natives/ZenNativeCaster.java new file mode 100644 index 0000000..c399110 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/natives/ZenNativeCaster.java @@ -0,0 +1,33 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type.natives; + +import stanhebben.zenscript.util.MethodOutput; + +/** + * + * @author Stanneke + */ +public class ZenNativeCaster { + private final Class owner; + private final Class output; + private final String methodName; + + public ZenNativeCaster(Class owner, Class output, String methodName) { + this.owner = owner; + this.output = output; + this.methodName = methodName; + } + + public Class getCasterClass() { + return output; + } + + public void compile(MethodOutput output) { + output.invoke(owner, methodName, this.output); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/natives/ZenNativeMember.java b/ZenScript/src/main/java/stanhebben/zenscript/type/natives/ZenNativeMember.java new file mode 100644 index 0000000..01bb9d4 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/natives/ZenNativeMember.java @@ -0,0 +1,165 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type.natives; + +import java.util.ArrayList; +import java.util.List; +import stanhebben.zenscript.compiler.IEnvironmentGlobal; +import stanhebben.zenscript.compiler.IEnvironmentMethod; +import stanhebben.zenscript.expression.Expression; +import stanhebben.zenscript.expression.ExpressionInvalid; +import stanhebben.zenscript.expression.partial.IPartialExpression; +import stanhebben.zenscript.symbols.IZenSymbol; +import stanhebben.zenscript.type.ZenType; +import stanhebben.zenscript.util.ZenPosition; + +/** + * + * @author Stan + */ +public class ZenNativeMember { + private JavaMethod getter; + private JavaMethod setter; + private final List methods = new ArrayList(); + + public JavaMethod getGetter() { + return getter; + } + + public JavaMethod getSetter() { + return setter; + } + + public void setGetter(JavaMethod getter) { + if (this.getter == null) { + this.getter = getter; + } else { + throw new RuntimeException("already has a getter"); + } + } + + public void setSetter(JavaMethod setter) { + if (this.setter == null) { + this.setter = setter; + } else { + throw new RuntimeException("already has a setter"); + } + } + + public IPartialExpression instance(ZenPosition position, IEnvironmentGlobal environment, IPartialExpression value) { + return new InstanceGetValue(position, value); + } + + public IPartialExpression instance(ZenPosition position, IEnvironmentGlobal environment) { + return new StaticGetValue(position); + } + + public void addMethod(JavaMethod method) { + methods.add(method); + } + + private class InstanceGetValue implements IPartialExpression { + private final ZenPosition position; + private final IPartialExpression value; + + public InstanceGetValue(ZenPosition position, IPartialExpression value) { + this.position = position; + this.value = value; + } + + @Override + public Expression eval(IEnvironmentGlobal environment) { + return getter.callVirtual(position, environment, value.eval(environment)); + } + + @Override + public Expression assign(ZenPosition position, IEnvironmentGlobal environment, Expression other) { + return setter.callVirtual(position, environment, value.eval(environment), other); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + ZenType type = environment.getType(getter.getMethod().getGenericReturnType()); + return type.getMember(position, environment, this, name); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentMethod environment, Expression... values) { + JavaMethod method = JavaMethod.select(false, methods, environment, values); + if (method == null) { + environment.error(position, "no matching method found"); + return new ExpressionInvalid(position); + } else { + return method.callVirtual(position, environment, value.eval(environment), values); + } + } + + @Override + public IZenSymbol toSymbol() { + return null; + } + + @Override + public ZenType toType(IEnvironmentGlobal environment) { + environment.error(position, "not a valid type"); + return ZenType.ANY; + } + } + + private class StaticGetValue implements IPartialExpression { + private final ZenPosition position; + + public StaticGetValue(ZenPosition position) { + this.position = position; + } + + @Override + public Expression eval(IEnvironmentGlobal environment) { + return setter.callStatic(position, environment); + } + + @Override + public Expression assign(ZenPosition position, IEnvironmentGlobal environment, Expression other) { + return setter.callStatic(position, environment, other); + } + + @Override + public IPartialExpression getMember(ZenPosition position, IEnvironmentGlobal environment, String name) { + ZenType type = environment.getType(getter.getMethod().getGenericReturnType()); + return type.getMember(position, environment, this, name); + } + + @Override + public Expression call(ZenPosition position, IEnvironmentMethod environment, Expression... values) { + JavaMethod method = JavaMethod.select(true, methods, environment, values); + if (method == null) { + environment.error(position, "no matching method found"); + return new ExpressionInvalid(position); + } else { + return method.callStatic(position, environment, values); + } + } + + @Override + public IZenSymbol toSymbol() { + return new StaticSymbol(); + } + + @Override + public ZenType toType(IEnvironmentGlobal environment) { + environment.error(position, "not a valid type"); + return ZenType.ANY; + } + } + + private class StaticSymbol implements IZenSymbol { + @Override + public IPartialExpression instance(ZenPosition position) { + return new StaticGetValue(position); + } + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/type/natives/ZenNativeOperator.java b/ZenScript/src/main/java/stanhebben/zenscript/type/natives/ZenNativeOperator.java new file mode 100644 index 0000000..b7a0aa5 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/type/natives/ZenNativeOperator.java @@ -0,0 +1,31 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.type.natives; + +import stanhebben.zenscript.annotations.OperatorType; + +/** + * + * @author Stanneke + */ +public class ZenNativeOperator { + private final OperatorType operator; + private final JavaMethod method; + + public ZenNativeOperator(OperatorType operator, JavaMethod method) { + this.operator = operator; + this.method = method; + } + + public OperatorType getOperator() { + return operator; + } + + public JavaMethod getMethod() { + return method; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/util/MethodCompiler.java b/ZenScript/src/main/java/stanhebben/zenscript/util/MethodCompiler.java new file mode 100644 index 0000000..ddbce15 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/util/MethodCompiler.java @@ -0,0 +1,15 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.util; + +/** + * + * @author Stanneke + */ +public interface MethodCompiler { + public void compile(MethodOutput output); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/util/MethodOutput.java b/ZenScript/src/main/java/stanhebben/zenscript/util/MethodOutput.java new file mode 100644 index 0000000..8b314a4 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/util/MethodOutput.java @@ -0,0 +1,617 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.util; + +import java.lang.reflect.Field; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import static org.objectweb.asm.Opcodes.*; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.LocalVariablesSorter; +import stanhebben.zenscript.type.natives.JavaMethod; +import static stanhebben.zenscript.util.ZenTypeUtil.internal; +import static stanhebben.zenscript.util.ZenTypeUtil.signature; + +/** + * + * @author Stanneke + */ +public class MethodOutput { + private final LocalVariablesSorter visitor; + + public MethodOutput(ClassVisitor cls, int access, String name, String descriptor, String signature, String[] exceptions) { + MethodVisitor methodVisitor = cls.visitMethod(access, name, descriptor, signature, exceptions); + visitor = new LocalVariablesSorter(access, descriptor, methodVisitor); + } + + public MethodOutput(LocalVariablesSorter visitor) { + this.visitor = visitor; + } + + public void start() { + visitor.visitCode(); + } + + public void end() { + visitor.visitMaxs(0, 0); + visitor.visitEnd(); + } + + public void label(Label label) { + visitor.visitLabel(label); + } + + public int local(Type type) { + return visitor.newLocal(type); + } + + public int local(Class cls) { + return visitor.newLocal(Type.getType(cls)); + } + + public void iConst0() { + visitor.visitInsn(ICONST_0); + } + + public void iConst1() { + visitor.visitInsn(ICONST_1); + } + + public void biPush(byte value) { + visitor.visitIntInsn(BIPUSH, value); + } + + public void siPush(short value) { + visitor.visitIntInsn(SIPUSH, value); + } + + public void aConstNull() { + visitor.visitInsn(ACONST_NULL); + } + + public void constant(Object value) { + visitor.visitLdcInsn(value); + } + + public void pop() { + visitor.visitInsn(POP); + } + + public void pop(boolean large) { + visitor.visitInsn(large ? POP2 : POP); + } + + public void dup() { + visitor.visitInsn(DUP); + } + + public void dup(boolean large) { + visitor.visitInsn(large ? DUP2 : DUP); + } + + public void dup2() { + visitor.visitInsn(DUP2); + } + + public void dupX1() { + visitor.visitInsn(DUP_X1); + } + + public void dupX2() { + visitor.visitInsn(DUP_X2); + } + + public void dup2X1() { + visitor.visitInsn(DUP2_X1); + } + + public void dup2X2() { + visitor.visitInsn(DUP2_X2); + } + + public void store(Type type, int local) { + visitor.visitVarInsn(type.getOpcode(ISTORE), local); + } + + public void load(Type type, int local) { + visitor.visitVarInsn(type.getOpcode(ILOAD), local); + } + + public void storeInt(int local) { + visitor.visitVarInsn(ISTORE, local); + } + + public void loadInt(int local) { + visitor.visitVarInsn(ILOAD, local); + } + + public void storeObject(int local) { + visitor.visitVarInsn(ASTORE, local); + } + + public void loadObject(int local) { + visitor.visitVarInsn(ALOAD, local); + } + + public void arrayLength() { + visitor.visitInsn(ARRAYLENGTH); + } + + public void arrayLoad(Type type) { + visitor.visitInsn(type.getOpcode(IALOAD)); + } + + public void arrayStore(Type type) { + visitor.visitInsn(type.getOpcode(IASTORE)); + } + + public void newArray(Type componentType) { + int sort = componentType.getSort(); + if (sort == Type.METHOD) { + throw new RuntimeException("Unsupported array type: " + componentType); + } else if (sort == Type.OBJECT || sort == Type.ARRAY) { + visitor.visitTypeInsn(ANEWARRAY, componentType.getInternalName()); + } else { + int type = 0; + switch (sort) { + case Type.BOOLEAN: type = Opcodes.T_BOOLEAN; break; + case Type.BYTE: type = Opcodes.T_BYTE; break; + case Type.SHORT: type = Opcodes.T_SHORT; break; + case Type.INT: type = Opcodes.T_INT; break; + case Type.LONG: type = Opcodes.T_LONG; break; + case Type.FLOAT: type = Opcodes.T_FLOAT; break; + case Type.DOUBLE: type = Opcodes.T_DOUBLE; break; + default: + throw new RuntimeException("Unsupported array type: " + componentType); + } + visitor.visitIntInsn(NEWARRAY, type); + } + } + + public void newArray(Class componentType) { + visitor.visitTypeInsn(NEWARRAY, internal(componentType)); + } + + public void checkCast(Class newClass) { + visitor.visitTypeInsn(CHECKCAST, signature(newClass)); + } + + public void iNeg() { + visitor.visitInsn(INEG); + } + + public void iAdd() { + visitor.visitInsn(IADD); + } + + public void iSub() { + visitor.visitInsn(ISUB); + } + + public void iMul() { + visitor.visitInsn(IMUL); + } + + public void iDiv() { + visitor.visitInsn(IDIV); + } + + public void iRem() { + visitor.visitInsn(IREM); + } + + public void iAnd() { + visitor.visitInsn(IAND); + } + + public void iOr() { + visitor.visitInsn(IOR); + } + + public void iXor() { + visitor.visitInsn(IXOR); + } + + public void iNot() { + visitor.visitInsn(ICONST_M1); + visitor.visitInsn(IXOR); + } + + public void lNeg() { + visitor.visitInsn(LNEG); + } + + public void lAdd() { + visitor.visitInsn(LADD); + } + + public void lSub() { + visitor.visitInsn(LSUB); + } + + public void lMul() { + visitor.visitInsn(LMUL); + } + + public void lDiv() { + visitor.visitInsn(LDIV); + } + + public void lRem() { + visitor.visitInsn(LREM); + } + + public void lAnd() { + visitor.visitInsn(LAND); + } + + public void lOr() { + visitor.visitInsn(LOR); + } + + public void lXor() { + visitor.visitInsn(LXOR); + } + + public void lNot() { + constant((long) -1); + lXor(); + } + + public void fNeg() { + visitor.visitInsn(FNEG); + } + + public void fAdd() { + visitor.visitInsn(FADD); + } + + public void fSub() { + visitor.visitInsn(FSUB); + } + + public void fMul() { + visitor.visitInsn(FMUL); + } + + public void fDiv() { + visitor.visitInsn(FDIV); + } + + public void fRem() { + visitor.visitInsn(FREM); + } + + public void dNeg() { + visitor.visitInsn(DNEG); + } + + public void dAdd() { + visitor.visitInsn(DADD); + } + + public void dSub() { + visitor.visitInsn(DSUB); + } + + public void dMul() { + visitor.visitInsn(DMUL); + } + + public void dDiv() { + visitor.visitInsn(DDIV); + } + + public void dRem() { + visitor.visitInsn(DREM); + } + + public void iinc(int local) { + visitor.visitIincInsn(local, 1); + } + + public void iinc(int local, int increment) { + visitor.visitIincInsn(local, increment); + } + + public void i2b() { + visitor.visitInsn(I2B); + } + + public void i2s() { + visitor.visitInsn(I2S); + } + + public void i2l() { + visitor.visitInsn(I2L); + } + + public void i2f() { + visitor.visitInsn(I2F); + } + + public void i2d() { + visitor.visitInsn(I2D); + } + + public void l2i() { + visitor.visitInsn(L2I); + } + + public void l2f() { + visitor.visitInsn(L2F); + } + + public void l2d() { + visitor.visitInsn(L2D); + } + + public void f2i() { + visitor.visitInsn(F2I); + } + + public void f2l() { + visitor.visitInsn(F2L); + } + + public void f2d() { + visitor.visitInsn(F2D); + } + + public void d2i() { + visitor.visitInsn(D2I); + } + + public void d2l() { + visitor.visitInsn(D2L); + } + + public void d2f() { + visitor.visitInsn(D2F); + } + + public void lCmp() { + visitor.visitInsn(LCMP); + } + + public void fCmp() { + visitor.visitInsn(FCMPL); + } + + public void dCmp() { + visitor.visitInsn(DCMPL); + } + + public void invokeStatic(String owner, String name, String descriptor) { + visitor.visitMethodInsn(INVOKESTATIC, owner, name, descriptor); + } + + public void invokeStatic(Class owner, String name, Class result, Class... arguments) { + StringBuilder descriptor = new StringBuilder(); + descriptor.append('('); + for (Class argument : arguments) { + descriptor.append(signature(argument)); + } + descriptor.append(')'); + descriptor.append(result == null ? 'V' : signature(result)); + visitor.visitMethodInsn(INVOKESTATIC, internal(owner), name, descriptor.toString()); + } + + public void invokeStatic(JavaMethod method) { + StringBuilder descriptor = new StringBuilder(); + descriptor.append('('); + for (Class argument : method.getMethod().getParameterTypes()) { + descriptor.append(signature(argument)); + } + descriptor.append(')'); + descriptor.append(signature(method.getMethod().getReturnType())); + visitor.visitMethodInsn(INVOKESTATIC, internal(method.getOwner()), method.getMethod().getName(), descriptor.toString()); + } + + public void invokeSpecial(String owner, String name, String descriptor) { + visitor.visitMethodInsn(INVOKESPECIAL, owner, name, descriptor); + } + + public void invoke(Class owner, String name, Class result, Class... arguments) { + if (owner.isInterface()) { + invokeInterface(owner, name, result, arguments); + } else { + invokeVirtual(owner, name, result, arguments); + } + } + + public void invoke(JavaMethod method) { + if (method.getOwner().isInterface()) { + invokeInterface( + method.getOwner(), + method.getMethod().getName(), + method.getMethod().getReturnType(), + method.getMethod().getParameterTypes()); + } else { + invokeVirtual( + method.getOwner(), + method.getMethod().getName(), + method.getMethod().getReturnType(), + method.getMethod().getParameterTypes()); + } + } + + public void invokeVirtual(String owner, String name, String descriptor) { + visitor.visitMethodInsn(INVOKEVIRTUAL, owner, name, descriptor); + } + + public void invokeVirtual(Class owner, String name, Class result, Class... arguments) { + StringBuilder descriptor = new StringBuilder(); + descriptor.append('('); + for (Class argument : arguments) { + descriptor.append(signature(argument)); + } + descriptor.append(')'); + descriptor.append(result == null ? 'V' : signature(result)); + visitor.visitMethodInsn(INVOKEVIRTUAL, internal(owner), name, descriptor.toString()); + } + + public void invokeInterface(String owner, String name, String descriptor) { + visitor.visitMethodInsn(INVOKEINTERFACE, owner, name, descriptor); + } + + public void invokeInterface(Class owner, String name, Class result, Class... arguments) { + StringBuilder descriptor = new StringBuilder(); + descriptor.append('('); + for (Class argument : arguments) { + descriptor.append(signature(argument)); + } + descriptor.append(')'); + descriptor.append(result == null ? 'V' : signature(result)); + visitor.visitMethodInsn(INVOKEINTERFACE, internal(owner), name, descriptor.toString()); + } + + public void newObject(Class type) { + visitor.visitTypeInsn(NEW, internal(type)); + } + + public void newObject(String type) { + visitor.visitTypeInsn(NEW, type); + } + + public void construct(Class type, Class... arguments) { + StringBuilder descriptor = new StringBuilder(); + descriptor.append('('); + for (Class argument : arguments) { + descriptor.append(signature(argument)); + } + descriptor.append(")V"); + visitor.visitMethodInsn(INVOKESPECIAL, internal(type), "", descriptor.toString()); + } + + public void construct(String type, String... arguments) { + StringBuilder descriptor = new StringBuilder(); + descriptor.append('('); + for (String argument : arguments) { + descriptor.append(argument); + } + descriptor.append(")V"); + visitor.visitMethodInsn(INVOKESPECIAL, type, "", descriptor.toString()); + } + + public void goTo(Label lbl) { + visitor.visitJumpInsn(GOTO, lbl); + } + + /** + * Jump if TOS == 0. + * + * @param lbl target label + */ + public void ifEQ(Label lbl) { + visitor.visitJumpInsn(IFEQ, lbl); + } + + public void ifNE(Label lbl) { + visitor.visitJumpInsn(IFNE, lbl); + } + + public void ifLT(Label lbl) { + visitor.visitJumpInsn(IFLT, lbl); + } + + public void ifGT(Label lbl) { + visitor.visitJumpInsn(IFGT, lbl); + } + + public void ifGE(Label lbl) { + visitor.visitJumpInsn(IFGE, lbl); + } + + public void ifLE(Label lbl) { + visitor.visitJumpInsn(IFLE, lbl); + } + + public void ifICmpLE(Label lbl) { + visitor.visitJumpInsn(IF_ICMPLE, lbl); + } + + public void ifICmpGE(Label lbl) { + visitor.visitJumpInsn(IF_ICMPGE, lbl); + } + + public void ifICmpEQ(Label lbl) { + visitor.visitJumpInsn(IF_ICMPEQ, lbl); + } + + public void ifICmpNE(Label lbl) { + visitor.visitJumpInsn(IF_ICMPNE, lbl); + } + + public void ifICmpGT(Label lbl) { + visitor.visitJumpInsn(IF_ICMPGT, lbl); + } + + public void ifICmpLT(Label lbl) { + visitor.visitJumpInsn(IF_ICMPLT, lbl); + } + + public void ifNull(Label lbl) { + visitor.visitJumpInsn(IFNULL, lbl); + } + + public void ifNonNull(Label lbl) { + visitor.visitJumpInsn(IFNONNULL, lbl); + } + + public void ret() { + visitor.visitInsn(RETURN); + } + + public void returnType(Type type) { + visitor.visitInsn(type.getOpcode(IRETURN)); + } + + public void returnInt() { + visitor.visitInsn(IRETURN); + } + + public void returnObject() { + visitor.visitInsn(ARETURN); + } + + public void getField(String owner, String name, String descriptor) { + visitor.visitFieldInsn(GETFIELD, owner, name, descriptor); + } + + public void getField(Class owner, String name, Class descriptor) { + visitor.visitFieldInsn(GETFIELD, internal(owner), name, signature(descriptor)); + } + + public void putField(String owner, String name, String descriptor) { + visitor.visitFieldInsn(PUTFIELD, owner, name, descriptor); + } + + public void putField(Class owner, String name, Class descriptor) { + visitor.visitFieldInsn(PUTFIELD, internal(owner), name, signature(descriptor)); + } + + public void getStaticField(Class owner, Field field) { + visitor.visitFieldInsn(GETSTATIC, internal(owner), field.getName(), signature(field.getType())); + } + + public void putStaticField(Class owner, Field field) { + visitor.visitFieldInsn(PUTSTATIC, internal(owner), field.getName(), signature(field.getType())); + } + + public void aThrow() { + visitor.visitInsn(ATHROW); + } + + public void position(ZenPosition position) { + Label label = new Label(); + visitor.visitLabel(label); + visitor.visitLineNumber(position.getLine(), label); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/util/StringUtil.java b/ZenScript/src/main/java/stanhebben/zenscript/util/StringUtil.java new file mode 100644 index 0000000..f414789 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/util/StringUtil.java @@ -0,0 +1,451 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.util; + +import java.util.ArrayList; + +/** + * + * @author Stanneke + */ +public class StringUtil { + private StringUtil() {} + + public static String join(String[] values, String separator) { + StringBuilder result = new StringBuilder(); + boolean first = true; + for (String value : values) { + if (first) { + first = false; + } else { + result.append('.'); + } + result.append(value); + } + return result.toString(); + } + + public static String join(Iterable values, String separator) { + StringBuilder result = new StringBuilder(); + boolean first = true; + for (String value : values) { + if (first) { + first = false; + } else { + result.append('.'); + } + result.append(value); + } + return result.toString(); + } + + /** + * Splits a string in parts, given a specified delimiter. + * + * @param value string to be split + * @param delimiter delimiter + * @return + */ + public static String[] split(String value, char delimiter) { + if (value == null) return null; + + ArrayList result = new ArrayList(); + int start = 0; + outer: for (int i = 0; i < value.length(); i++) { + if (value.charAt(i) == delimiter) { + result.add(value.substring(start, i)); + start = i + 1; + } + } + result.add(value.substring(start)); + return result.toArray(new String[result.size()]); + } + + // ######################## + // ### String utilities ### + // ######################## + + /** + * + * unescape_perl_string() + * + * Tom Christiansen Sun Nov 28 12:55:24 MST 2010 + * + * It's completely ridiculous that there's no standard unescape_java_string + * function. Since I have to do the damn thing myself, I might as well make + * it halfway useful by supporting things Java was too stupid to consider in + * strings: + * + * => "?" items are additions to Java string escapes but normal in Java + * regexes + * + * => "!" items are also additions to Java regex escapes + * + * Standard singletons: ?\a ?\e \f \n \r \t + * + * NB: \b is unsupported as backspace so it can pass-through to the regex + * translator untouched; I refuse to make anyone doublebackslash it as + * doublebackslashing is a Java idiocy I desperately wish would die out. + * There are plenty of other ways to write it: + * + * \cH, \12, \012, \x08 \x{8}, \u0008, \U00000008 + * + * Octal escapes: \0 \0N \0NN \N \NN \NNN Can range up to !\777 not \377 + * + * TODO: add !\o{NNNNN} last Unicode is 4177777 maxint is 37777777777 + * + * Control chars: ?\cX Means: ord(X) ^ ord('@') + * + * Old hex escapes: \xXX unbraced must be 2 xdigits + * + * Perl hex escapes: !\x{XXX} braced may be 1-8 xdigits NB: proper Unicode + * never needs more than 6, as highest valid codepoint is 0x10FFFF, not + * maxint 0xFFFFFFFF + * + * Lame Java escape: \[IDIOT JAVA PREPROCESSOR]uXXXX must be exactly 4 + * xdigits; + * + * I can't write XXXX in this comment where it belongs because the damned + * Java Preprocessor can't mind its own business. Idiots! + * + * Lame Python escape: !\UXXXXXXXX must be exactly 8 xdigits + * + * TODO: Perl translation escapes: \Q \U \L \E \[IDIOT JAVA PREPROCESSOR]u + * \l These are not so important to cover if you're passing the result to + * Pattern.compile(), since it handles them for you further downstream. Hm, + * what about \[IDIOT JAVA PREPROCESSOR]u? + * + * @param oldstr + * @return + */ + public static String unescapeString(String oldstr) { + if (oldstr.charAt(0) != '"' || oldstr.charAt(oldstr.length() - 1) != '"') { + // TODO: error + //throw new TweakerExecuteException("Not a valid string constant: " + // + oldstr); + } + oldstr = oldstr.substring(1, oldstr.length() - 1); + + /* + * In contrast to fixing Java's broken regex charclasses, this one need + * be no bigger, as unescaping shrinks the string here, where in the + * other one, it grows it. + */ + + StringBuilder newstr = new StringBuilder(oldstr.length()); + + boolean saw_backslash = false; + + for (int i = 0; i < oldstr.length(); i++) { + int cp = oldstr.codePointAt(i); + if (oldstr.codePointAt(i) > Character.MAX_VALUE) { + i++; + /**** WE HATES UTF-16! WE HATES IT FOREVERSES!!! ****/ + } + + if (!saw_backslash) { + if (cp == '\\') { + saw_backslash = true; + } else { + newstr.append(Character.toChars(cp)); + } + continue; /* switch */ + } + + if (cp == '\\') { + saw_backslash = false; + newstr.append('\\'); + newstr.append('\\'); + continue; /* switch */ + } + + switch (cp) { + + case 'r': + newstr.append('\r'); + break; /* switch */ + + case 'n': + newstr.append('\n'); + break; /* switch */ + + case 'f': + newstr.append('\f'); + break; /* switch */ + + /* PASS a \b THROUGH!! */ + case 'b': + newstr.append("\\b"); + break; /* switch */ + + case 't': + newstr.append('\t'); + break; /* switch */ + + case 'a': + newstr.append('\007'); + break; /* switch */ + + case 'e': + newstr.append('\033'); + break; /* switch */ + + /* + * A "control" character is what you get when you xor its codepoint + * with '@'==64. This only makes sense for ASCII, and may not yield + * a "control" character after all. + * + * Strange but true: "\c{" is ";", "\c}" is "=", etc. + */ + case 'c': { + if (++i == oldstr.length()) { + // TODO: error + //throw new TweakerExecuteException("trailing \\c"); + } + cp = oldstr.codePointAt(i); + /* + * don't need to grok surrogates, as next line blows them up + */ + if (cp > 0x7f) { + // TODO: error + //throw new TweakerExecuteException( + // "expected ASCII after \\c"); + } + newstr.append(Character.toChars(cp ^ 64)); + break; /* switch */ + } + + case '8': + case '9': + // TODO: error + //throw new TweakerExecuteException("illegal octal digit"); + /* NOTREACHED */ + + /* + * may be 0 to 2 octal digits following this one so back up one + * for fallthrough to next case; unread this digit and fall + * through to next case. + */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + --i; + /* FALLTHROUGH */ + + /* + * Can have 0, 1, or 2 octal digits following a 0 this permits + * larger values than octal 377, up to octal 777. + */ + case '0': { + if (i + 1 == oldstr.length()) { + /* found \0 at end of string */ + newstr.append(Character.toChars(0)); + break; /* switch */ + } + i++; + int digits = 0; + int j; + for (j = 0; j <= 2; j++) { + if (i + j == oldstr.length()) { + break; /* for */ + } + /* safe because will unread surrogate */ + int ch = oldstr.charAt(i + j); + if (ch < '0' || ch > '7') { + break; /* for */ + } + digits++; + } + if (digits == 0) { + --i; + newstr.append('\0'); + break; /* switch */ + } + int value = 0; + try { + value = Integer + .parseInt(oldstr.substring(i, i + digits), 8); + } catch (NumberFormatException nfe) { + // TODO: error + //throw new TweakerExecuteException( + // "invalid octal value for \\0 escape"); + } + newstr.append(Character.toChars(value)); + i += digits - 1; + break; /* switch */ + } /* end case '0' */ + + case 'x': { + if (i + 2 > oldstr.length()) { + // TODO: error + //throw new TweakerExecuteException( + // "string too short for \\x escape"); + } + i++; + boolean saw_brace = false; + if (oldstr.charAt(i) == '{') { + /* ^^^^^^ ok to ignore surrogates here */ + i++; + saw_brace = true; + } + int j; + for (j = 0; j < 8; j++) { + + if (!saw_brace && j == 2) { + break; /* for */ + } + + /* + * ASCII test also catches surrogates + */ + int ch = oldstr.charAt(i + j); + if (ch > 127) { + // TODO: error + //throw new TweakerExecuteException( + // "illegal non-ASCII hex digit in \\x escape"); + } + + if (saw_brace && ch == '}') { + break; /* for */ + } + + if (!((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'))) { + // TODO: error + //throw new TweakerExecuteException(String.format( + // "illegal hex digit #%d '%c' in \\x", ch, ch)); + } + + } + if (j == 0) { + // TODO: error + //throw new TweakerExecuteException( + // "empty braces in \\x{} escape"); + } + int value = 0; + try { + value = Integer.parseInt(oldstr.substring(i, i + j), 16); + } catch (NumberFormatException nfe) { + // TODO: error + //throw new TweakerExecuteException( + // "invalid hex value for \\x escape"); + } + newstr.append(Character.toChars(value)); + if (saw_brace) { + j++; + } + i += j - 1; + break; /* switch */ + } + + case 'u': { + if (i + 4 > oldstr.length()) { + // TODO: error + //throw new TweakerExecuteException( + // "string too short for \\u escape"); + } + i++; + int j; + for (j = 0; j < 4; j++) { + /* this also handles the surrogate issue */ + if (oldstr.charAt(i + j) > 127) { + // TODO: error + //throw new TweakerExecuteException( + // "illegal non-ASCII hex digit in \\u escape"); + } + } + int value = 0; + try { + value = Integer.parseInt(oldstr.substring(i, i + j), 16); + } catch (NumberFormatException nfe) { + // TODO: error + //throw new TweakerExecuteException( + // "invalid hex value for \\u escape"); + } + newstr.append(Character.toChars(value)); + i += j - 1; + break; /* switch */ + } + + case 'U': { + if (i + 8 > oldstr.length()) { + // TODO: error + //throw new TweakerExecuteException( + // "string too short for \\U escape"); + } + i++; + int j; + for (j = 0; j < 8; j++) { + /* this also handles the surrogate issue */ + if (oldstr.charAt(i + j) > 127) { + // TODO: error + //throw new TweakerExecuteException( + // "illegal non-ASCII hex digit in \\U escape"); + } + } + int value = 0; + try { + value = Integer.parseInt(oldstr.substring(i, i + j), 16); + } catch (NumberFormatException nfe) { + // TODO: error + //throw new TweakerExecuteException( + // "invalid hex value for \\U escape"); + } + newstr.append(Character.toChars(value)); + i += j - 1; + break; /* switch */ + } + + default: + newstr.append('\\'); + newstr.append(Character.toChars(cp)); + /* + * say(String.format( + * "DEFAULT unrecognized escape %c passed through", cp)); + */ + break; /* switch */ + + } + saw_backslash = false; + } + + /* weird to leave one at the end */ + if (saw_backslash) { + newstr.append('\\'); + } + + return newstr.toString(); + } + + /* + * Return a string "U+XX.XXX.XXXX" etc, where each XX set is the xdigits of + * the logical Unicode code point. No bloody brain-damaged UTF-16 surrogate + * crap, just true logical characters. + */ + private static String uniplus(String s) { + if (s.length() == 0) { + return ""; + } + /* This is just the minimum; sb will grow as needed. */ + StringBuilder sb = new StringBuilder(2 + 3 * s.length()); + sb.append("U+"); + for (int i = 0; i < s.length(); i++) { + sb.append(String.format("%X", s.codePointAt(i))); + if (s.codePointAt(i) > Character.MAX_VALUE) { + i++; + /**** WE HATES UTF-16! WE HATES IT FOREVERSES!!! ****/ + } + if (i + 1 < s.length()) { + sb.append("."); + } + } + return sb.toString(); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/util/ZenPosition.java b/ZenScript/src/main/java/stanhebben/zenscript/util/ZenPosition.java new file mode 100644 index 0000000..ef1d08d --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/util/ZenPosition.java @@ -0,0 +1,42 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.util; + +import stanhebben.zenscript.ZenParsedFile; + +/** + * + * @author Stanneke + */ +public class ZenPosition { + private final ZenParsedFile file; + private final int line; + private final int offset; + + public ZenPosition(ZenParsedFile file, int line, int offset) { + this.file = file; + this.line = line; + this.offset = offset; + } + + public ZenParsedFile getFile() { + return file; + } + + public int getLine() { + return line; + } + + public int getLineOffset() { + return offset; + } + + @Override + public String toString() { + return Integer.toString(line) + ":" + offset; + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/util/ZenTypeUtil.java b/ZenScript/src/main/java/stanhebben/zenscript/util/ZenTypeUtil.java new file mode 100644 index 0000000..cea6e02 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/util/ZenTypeUtil.java @@ -0,0 +1,58 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.util; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author Stanneke + */ +public class ZenTypeUtil { + private static final Map SIGNATURE_MAP; + + static { + SIGNATURE_MAP = new HashMap(); + SIGNATURE_MAP.put(boolean.class.getName(), "Z"); + SIGNATURE_MAP.put(byte.class.getName(), "B"); + SIGNATURE_MAP.put(short.class.getName(), "S"); + SIGNATURE_MAP.put(int.class.getName(), "I"); + SIGNATURE_MAP.put(long.class.getName(), "J"); + SIGNATURE_MAP.put(float.class.getName(), "F"); + SIGNATURE_MAP.put(double.class.getName(), "D"); + SIGNATURE_MAP.put(char.class.getName(), "C"); + SIGNATURE_MAP.put(void.class.getName(), "V"); + } + + public static String signature(Class cls) { + if (SIGNATURE_MAP.containsKey(cls.getName())) { + return SIGNATURE_MAP.get(cls.getName()); + } else if (cls.isArray()) { + return "[" + signature(cls.getComponentType()); + } else { + String signature = "L" + internal(cls) + ";"; + SIGNATURE_MAP.put(cls.getName(), signature); + return signature; + } + } + + public static String internal(Class cls) { + return cls.getName().replace('.', '/'); + } + + public static String descriptor(Method method) { + StringBuilder output = new StringBuilder(); + output.append('('); + for (Class param : method.getParameterTypes()) { + output.append(signature(param)); + } + output.append(')').append(signature(method.getReturnType())); + return output.toString(); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/value/IAny.java b/ZenScript/src/main/java/stanhebben/zenscript/value/IAny.java new file mode 100644 index 0000000..16af91b --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/value/IAny.java @@ -0,0 +1,73 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.value; + +import java.util.Iterator; + +/** + * + * @author Stanneke + */ +public interface IAny { + public IAny not(); + + public IAny neg(); + + public IAny add(IAny value); + + public IAny sub(IAny value); + + public IAny cat(IAny value); + + public IAny mul(IAny value); + + public IAny div(IAny value); + + public IAny mod(IAny value); + + public IAny and(IAny value); + + public IAny or(IAny value); + + public IAny xor(IAny value); + + public IAny range(IAny value); + + public int compareTo(IAny value); + + public boolean contains(IAny value); + + public IAny member(String value); + + public IAny indexGet(IAny key); + + public void indexSet(IAny key, IAny value); + + public IAny call(IAny... values); + + public boolean asBool(); + + public byte asByte(); + + public short asShort(); + + public int asInt(); + + public long asLong(); + + public float asFloat(); + + public double asDouble(); + + public String asString(); + + public T as(Class cls); + + public Iterator iteratorSingle(); + + public Iterator iteratorMulti(int n); +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyByte.java b/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyByte.java new file mode 100644 index 0000000..04dfe6f --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyByte.java @@ -0,0 +1,177 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.value.builtin; + +import java.util.Iterator; +import stanhebben.zenscript.ZenRuntimeException; +import stanhebben.zenscript.value.IAny; + +/** + * + * @author Stanneke + */ +public class AnyByte implements IAny { + public static AnyByte valueOf(byte value) { + return new AnyByte(value); + } + + private final byte value; + + public AnyByte(byte value) { + this.value = value; + } + + @Override + public IAny not() { + return new AnyByte((byte) ~value); + } + + @Override + public IAny neg() { + return new AnyByte((byte) -value); + } + + @Override + public IAny add(IAny value) { + return new AnyByte((byte) (this.value + value.asByte())); + } + + @Override + public IAny sub(IAny value) { + return new AnyByte((byte) (this.value - value.asByte())); + } + + @Override + public IAny cat(IAny value) { + return new AnyString(Byte.toString(this.value) + value.asString()); + } + + @Override + public IAny mul(IAny value) { + return new AnyByte((byte) (this.value * value.asByte())); + } + + @Override + public IAny div(IAny value) { + return new AnyByte((byte) (this.value / value.asByte())); + } + + @Override + public IAny mod(IAny value) { + return new AnyByte((byte) (this.value % value.asByte())); + } + + @Override + public IAny and(IAny value) { + return new AnyByte((byte) (this.value & value.asByte())); + } + + @Override + public IAny or(IAny value) { + return new AnyByte((byte) (this.value | value.asByte())); + } + + @Override + public IAny xor(IAny value) { + return new AnyByte((byte) (this.value ^ value.asByte())); + } + + @Override + public IAny range(IAny value) { + //return new AnyIntRange(this.value, value.asInt()); + return null; // TODO + } + + @Override + public int compareTo(IAny value) { + return Integer.compare(this.value, value.asByte()); + } + + @Override + public boolean contains(IAny value) { + throw new ZenRuntimeException("byte has no in operator"); + } + + @Override + public IAny member(String value) { + if (value.equals("abs")) { + return new AnyByte((byte) (Math.abs(this.value))); + } else { + throw new ZenRuntimeException("no such member in byte: " + value); + } + } + + @Override + public IAny indexGet(IAny key) { + throw new ZenRuntimeException("cannot index a byte value"); + } + + @Override + public void indexSet(IAny key, IAny value) { + throw new ZenRuntimeException("cannot index a byte value"); + } + + @Override + public IAny call(IAny... values) { + throw new ZenRuntimeException("cannot index a byte value"); + } + + @Override + public boolean asBool() { + throw new ZenRuntimeException("cannot convert a byte to a bool"); + } + + @Override + public byte asByte() { + return value; + } + + @Override + public short asShort() { + return value; + } + + @Override + public int asInt() { + return value; + } + + @Override + public long asLong() { + return value; + } + + @Override + public float asFloat() { + return value; + } + + @Override + public double asDouble() { + return value; + } + + @Override + public String asString() { + return Byte.toString(value); + } + + @Override + public T as(Class cls) { + throw new ZenRuntimeException("cannot convert a byte to an object"); + } + + @Override + public Iterator iteratorSingle() { + throw new ZenRuntimeException("cannot iterate over a byte value"); + } + + @Override + public Iterator iteratorMulti(int n) { + throw new ZenRuntimeException("cannot iterate over a byte value"); + } +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyIntRange.java b/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyIntRange.java new file mode 100644 index 0000000..88ca801 --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyIntRange.java @@ -0,0 +1,163 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.value.builtin; + +import java.util.Iterator; +import stanhebben.zenscript.value.IAny; + +/** + * + * @author Stanneke + */ +public class AnyIntRange implements IAny { + + @Override + public IAny not() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IAny neg() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IAny add(IAny value) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IAny sub(IAny value) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IAny cat(IAny value) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IAny mul(IAny value) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IAny div(IAny value) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IAny mod(IAny value) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IAny and(IAny value) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IAny or(IAny value) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IAny xor(IAny value) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IAny range(IAny value) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public int compareTo(IAny value) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public boolean contains(IAny value) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IAny member(String value) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IAny indexGet(IAny key) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public void indexSet(IAny key, IAny value) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IAny call(IAny... values) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public boolean asBool() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public byte asByte() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public short asShort() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public int asInt() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public long asLong() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public float asFloat() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public double asDouble() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public String asString() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public T as(Class cls) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Iterator iteratorSingle() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Iterator iteratorMulti(int n) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + +} diff --git a/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyString.java b/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyString.java new file mode 100644 index 0000000..31c5bcc --- /dev/null +++ b/ZenScript/src/main/java/stanhebben/zenscript/value/builtin/AnyString.java @@ -0,0 +1,192 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package stanhebben.zenscript.value.builtin; + +import java.util.Iterator; +import stanhebben.zenscript.ZenRuntimeException; +import stanhebben.zenscript.value.IAny; + +/** + * + * @author Stanneke + */ +public class AnyString implements IAny { + private final String value; + + public AnyString(String value) { + this.value = value; + } + + @Override + public IAny not() { + throw new ZenRuntimeException("cannot invert a string value"); + } + + @Override + public IAny neg() { + throw new ZenRuntimeException("cannot negate a string value"); + } + + @Override + public IAny add(IAny value) { + throw new ZenRuntimeException("cannot add to a string value (did you intend to use ~ instead?)"); + } + + @Override + public IAny sub(IAny value) { + throw new ZenRuntimeException("cannot subtract from a string"); + } + + @Override + public IAny cat(IAny value) { + return new AnyString(this.value + value.asString()); + } + + @Override + public IAny mul(IAny value) { + throw new ZenRuntimeException("cannot multiply a string value"); + } + + @Override + public IAny div(IAny value) { + throw new ZenRuntimeException("cannot divide a string value"); + } + + @Override + public IAny mod(IAny value) { + throw new ZenRuntimeException("cannot modulo a string value"); + } + + @Override + public IAny and(IAny value) { + throw new ZenRuntimeException("cannot and with a string value"); + } + + @Override + public IAny or(IAny value) { + throw new ZenRuntimeException("cannot or with a string value"); + } + + @Override + public IAny xor(IAny value) { + throw new ZenRuntimeException("cannot xor with a string value"); + } + + @Override + public IAny range(IAny value) { + throw new ZenRuntimeException("cannot range a string value"); + } + + @Override + public int compareTo(IAny value) { + return this.value.compareTo(value.asString()); + } + + @Override + public boolean contains(IAny value) { + return this.value.indexOf(value.asString()) >= 0; + } + + @Override + public IAny member(String value) { + throw new ZenRuntimeException("strings have no members (yet)"); + } + + @Override + public IAny indexGet(IAny key) { + throw new ZenRuntimeException("cannot index a string (yet)"); + } + + @Override + public void indexSet(IAny key, IAny value) { + throw new ZenRuntimeException("cannot modify a string"); + } + + @Override + public IAny call(IAny... values) { + throw new ZenRuntimeException("cannot call a string"); + } + + @Override + public boolean asBool() { + return value != null; + } + + @Override + public byte asByte() { + try { + return Byte.parseByte(value); + } catch (NumberFormatException ex) { + throw new ZenRuntimeException("Cannot convert " + value + " to a byte value"); + } + } + + @Override + public short asShort() { + try { + return Short.parseShort(value); + } catch (NumberFormatException ex) { + throw new ZenRuntimeException("Cannot convert " + value + " to a short value"); + } + } + + @Override + public int asInt() { + try { + return Integer.parseInt(value); + } catch (NumberFormatException ex) { + throw new ZenRuntimeException("Cannot convert " + value + " to an int value"); + } + } + + @Override + public long asLong() { + try { + return Long.parseLong(value); + } catch (NumberFormatException ex) { + throw new ZenRuntimeException("Cannot convert " + value + " to a long value"); + } + } + + @Override + public float asFloat() { + try { + return Float.parseFloat(value); + } catch (NumberFormatException ex) { + throw new ZenRuntimeException("Cannot convert " + value + " to a float value"); + } + } + + @Override + public double asDouble() { + try { + return Double.parseDouble(value); + } catch (NumberFormatException ex) { + throw new ZenRuntimeException("Cannot convert " + value + " to a double value"); + } + } + + @Override + public String asString() { + return value; + } + + @Override + public T as(Class cls) { + throw new ZenRuntimeException("Cannot convert a string to an object"); + } + + @Override + public Iterator iteratorSingle() { + throw new ZenRuntimeException("Cannot iterate over a string (yet)"); + } + + @Override + public Iterator iteratorMulti(int n) { + throw new ZenRuntimeException("Cannot iterate over a string (yet)"); + } +} diff --git a/common.gradle b/common.gradle index 34d24fe..f5283a7 100644 --- a/common.gradle +++ b/common.gradle @@ -1,56 +1,29 @@ -// -// This file is to be applied to every subproject. -// - -apply plugin: 'java' -apply plugin: 'maven' - -String mavenGroupId = 'stanhebben.minetweaker' -String mavenVersion = '3.0' - -sourceCompatibility = '1.8' -[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' repositories { - mavenCentral(); - // You may define additional repositories, or even remove "mavenCentral()". - // Read more about repositories here: - // http://www.gradle.org/docs/current/userguide/dependency_management.html#sec:repositories + mavenCentral() + maven { + name = "forge" + url = "http://files.minecraftforge.net/maven" + } + maven { + name = "sonatype" + url = "https://oss.sonatype.org/content/repositories/snapshots/" + } } -dependencies { - // Adding dependencies here will add the dependencies to each subproject. - testCompile group: 'junit', name: 'junit', version: '4.10' -} - -String mavenArtifactId = name - -group = mavenGroupId -version = mavenVersion - -task sourcesJar(type: Jar, dependsOn: classes, description: 'Creates a jar from the source files.') { - classifier = 'sources' - from sourceSets.main.allSource -} - -artifacts { - archives jar - archives sourcesJar -} - -configure(install.repositories.mavenInstaller) { - pom.project { - groupId = mavenGroupId - artifactId = mavenArtifactId - version = mavenVersion - } -} - -task createFolders(description: 'Creates the source folders if they do not exist.') doLast { - sourceSets*.allSource*.srcDirs*.each { File srcDir -> - if (!srcDir.isDirectory()) { - println "Creating source folder: ${srcDir}" - srcDir.mkdirs() +buildscript { + repositories { + mavenCentral() + maven { + name = "forge" + url = "http://files.minecraftforge.net/maven" + } + maven { + name = "sonatype" + url = "https://oss.sonatype.org/content/repositories/snapshots/" } } + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT' + } }