commit ddf9ed86a75b5486f598b16aa716a85d7fc9148b Author: dfsek Date: Sun Oct 17 15:41:05 2021 -0700 Initial commit diff --git a/common/addons/structure-terrascript-loader/README.md b/common/addons/structure-terrascript-loader/README.md new file mode 100644 index 000000000..5bf8f622a --- /dev/null +++ b/common/addons/structure-terrascript-loader/README.md @@ -0,0 +1,4 @@ +# structure-terrascript-loader + +Implements the TerraScript structure scripting language, and loads all `*.tesf` +files into the Structure registry. \ No newline at end of file diff --git a/common/addons/structure-terrascript-loader/build.gradle.kts b/common/addons/structure-terrascript-loader/build.gradle.kts new file mode 100644 index 000000000..e71d4b285 --- /dev/null +++ b/common/addons/structure-terrascript-loader/build.gradle.kts @@ -0,0 +1,18 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +plugins { + id("com.github.johnrengelman.shadow") +} + +dependencies { + "shadedApi"("commons-io:commons-io:2.6") +} + +tasks.named("shadowJar") { + archiveClassifier.set("") + relocate("org.apache.commons", "com.dfsek.terra.addons.terrascript.lib.commons") +} + +tasks.named("build") { + finalizedBy(tasks.named("shadowJar")) +} \ No newline at end of file diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/TerraScriptAddon.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/TerraScriptAddon.java new file mode 100644 index 000000000..727944e83 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/TerraScriptAddon.java @@ -0,0 +1,55 @@ +package com.dfsek.terra.addons.terrascript; + +import com.dfsek.tectonic.exception.LoadException; + +import java.io.InputStream; +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.script.StructureScript; +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.addon.TerraAddon; +import com.dfsek.terra.api.addon.annotations.Addon; +import com.dfsek.terra.api.addon.annotations.Author; +import com.dfsek.terra.api.addon.annotations.Version; +import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent; +import com.dfsek.terra.api.event.functional.FunctionalEventHandler; +import com.dfsek.terra.api.inject.annotations.Inject; +import com.dfsek.terra.api.registry.CheckedRegistry; +import com.dfsek.terra.api.structure.LootTable; +import com.dfsek.terra.api.structure.Structure; +import com.dfsek.terra.api.util.StringUtil; + + +@Addon("structure-terrascript-loader") +@Author("Terra") +@Version("1.0.0") +public class TerraScriptAddon extends TerraAddon { + @Inject + private Platform platform; + + @Override + public void initialize() { + platform.getEventManager() + .getHandler(FunctionalEventHandler.class) + .register(this, ConfigPackPreLoadEvent.class) + .then(event -> { + CheckedRegistry structureRegistry = event.getPack().getOrCreateRegistry(Structure.class); + CheckedRegistry lootRegistry = event.getPack().getOrCreateRegistry(LootTable.class); + event.getPack().getLoader().open("", ".tesf").thenEntries(entries -> { + for(Map.Entry entry : entries) { + try { + String id = StringUtil.fileName(entry.getKey()); + StructureScript structureScript = new StructureScript(entry.getValue(), id, platform, structureRegistry, + lootRegistry, + event.getPack().getRegistryFactory().create()); + structureRegistry.register(structureScript.getID(), structureScript); + } catch(ParseException e) { + throw new LoadException("Failed to load script \"" + entry.getKey() + "\"", e); + } + } + }).close(); + }) + .failThrough(); + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/buffer/IntermediateBuffer.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/buffer/IntermediateBuffer.java new file mode 100644 index 000000000..3c2af58c6 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/buffer/IntermediateBuffer.java @@ -0,0 +1,49 @@ +package com.dfsek.terra.addons.terrascript.buffer; + +import com.dfsek.terra.api.structure.buffer.Buffer; +import com.dfsek.terra.api.structure.buffer.BufferedItem; +import com.dfsek.terra.api.util.vector.Vector3; +import com.dfsek.terra.api.world.Chunk; +import com.dfsek.terra.api.world.World; + + +public class IntermediateBuffer implements Buffer { + private final Buffer original; + private final Vector3 offset; + + public IntermediateBuffer(Buffer original, Vector3 offset) { + this.original = original; + this.offset = offset.clone(); + } + + @Override + public void paste(Vector3 origin, Chunk chunk) { + // no-op + } + + @Override + public void paste(Vector3 origin, World world) { + // no-op + } + + @Override + public Buffer addItem(BufferedItem item, Vector3 location) { + return original.addItem(item, location.clone().add(offset)); + } + + @Override + public Buffer setMark(String mark, Vector3 location) { + original.setMark(mark, location.clone().add(offset)); + return this; + } + + @Override + public Vector3 getOrigin() { + return original.getOrigin().clone().add(offset); + } + + @Override + public String getMark(Vector3 location) { + return original.getMark(location.clone().add(offset)); + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/buffer/items/BufferedEntity.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/buffer/items/BufferedEntity.java new file mode 100644 index 000000000..7676c088f --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/buffer/items/BufferedEntity.java @@ -0,0 +1,27 @@ +package com.dfsek.terra.addons.terrascript.buffer.items; + +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.entity.Entity; +import com.dfsek.terra.api.entity.EntityType; +import com.dfsek.terra.api.event.events.world.generation.EntitySpawnEvent; +import com.dfsek.terra.api.structure.buffer.BufferedItem; +import com.dfsek.terra.api.util.vector.Vector3; +import com.dfsek.terra.api.world.World; + + +public class BufferedEntity implements BufferedItem { + + private final EntityType type; + private final Platform platform; + + public BufferedEntity(EntityType type, Platform platform) { + this.type = type; + this.platform = platform; + } + + @Override + public void paste(Vector3 origin, World world) { + Entity entity = world.spawnEntity(origin.clone().add(0.5, 0, 0.5), type); + platform.getEventManager().callEvent(new EntitySpawnEvent(entity.world().getConfig().getPack(), entity)); + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/buffer/items/BufferedLootApplication.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/buffer/items/BufferedLootApplication.java new file mode 100644 index 000000000..98e82c736 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/buffer/items/BufferedLootApplication.java @@ -0,0 +1,48 @@ +package com.dfsek.terra.addons.terrascript.buffer.items; + +import java.util.Random; + +import com.dfsek.terra.addons.terrascript.script.StructureScript; +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.block.entity.BlockEntity; +import com.dfsek.terra.api.block.entity.Container; +import com.dfsek.terra.api.event.events.world.generation.LootPopulateEvent; +import com.dfsek.terra.api.structure.LootTable; +import com.dfsek.terra.api.structure.buffer.BufferedItem; +import com.dfsek.terra.api.util.vector.Vector3; +import com.dfsek.terra.api.world.World; + + +public class BufferedLootApplication implements BufferedItem { + private final LootTable table; + private final Platform platform; + private final StructureScript structure; + + public BufferedLootApplication(LootTable table, Platform platform, StructureScript structure) { + this.table = table; + this.platform = platform; + this.structure = structure; + } + + @Override + public void paste(Vector3 origin, World world) { + try { + BlockEntity data = world.getBlockState(origin); + if(!(data instanceof Container)) { + platform.logger().severe("Failed to place loot at " + origin + "; block " + data + " is not container."); + return; + } + Container container = (Container) data; + + LootPopulateEvent event = new LootPopulateEvent(container, table, world.getConfig().getPack(), structure); + platform.getEventManager().callEvent(event); + if(event.isCancelled()) return; + + event.getTable().fillInventory(container.getInventory(), new Random(origin.hashCode())); + data.update(false); + } catch(Exception e) { + platform.logger().warning("Could not apply loot at " + origin + ": " + e.getMessage()); + e.printStackTrace(); + } + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/buffer/items/BufferedPulledBlock.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/buffer/items/BufferedPulledBlock.java new file mode 100644 index 000000000..5ce6982b5 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/buffer/items/BufferedPulledBlock.java @@ -0,0 +1,27 @@ +package com.dfsek.terra.addons.terrascript.buffer.items; + +import com.dfsek.terra.api.block.state.BlockState; +import com.dfsek.terra.api.structure.buffer.BufferedItem; +import com.dfsek.terra.api.util.vector.Vector3; +import com.dfsek.terra.api.world.World; + + +public class BufferedPulledBlock implements BufferedItem { + private final BlockState data; + + public BufferedPulledBlock(BlockState data) { + this.data = data; + } + + @Override + public void paste(Vector3 origin, World world) { + Vector3 mutable = origin.clone(); + while(mutable.getY() > world.getMinHeight()) { + if(!world.getBlockData(mutable).isAir()) { + world.setBlockData(mutable, data); + break; + } + mutable.subtract(0, 1, 0); + } + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/buffer/items/BufferedStateManipulator.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/buffer/items/BufferedStateManipulator.java new file mode 100644 index 000000000..5d863f0cb --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/buffer/items/BufferedStateManipulator.java @@ -0,0 +1,30 @@ +package com.dfsek.terra.addons.terrascript.buffer.items; + +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.block.entity.BlockEntity; +import com.dfsek.terra.api.structure.buffer.BufferedItem; +import com.dfsek.terra.api.util.vector.Vector3; +import com.dfsek.terra.api.world.World; + + +public class BufferedStateManipulator implements BufferedItem { + private final Platform platform; + private final String data; + + public BufferedStateManipulator(Platform platform, String state) { + this.platform = platform; + this.data = state; + } + + @Override + public void paste(Vector3 origin, World world) { + try { + BlockEntity state = world.getBlockState(origin); + state.applyState(data); + state.update(false); + } catch(Exception e) { + platform.logger().warning("Could not apply BlockState at " + origin + ": " + e.getMessage()); + e.printStackTrace(); + } + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/Parser.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/Parser.java new file mode 100644 index 000000000..f043737b1 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/Parser.java @@ -0,0 +1,430 @@ +package com.dfsek.terra.addons.terrascript.parser; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.Item; +import com.dfsek.terra.addons.terrascript.parser.lang.Keyword; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.constants.BooleanConstant; +import com.dfsek.terra.addons.terrascript.parser.lang.constants.ConstantExpression; +import com.dfsek.terra.addons.terrascript.parser.lang.constants.NumericConstant; +import com.dfsek.terra.addons.terrascript.parser.lang.constants.StringConstant; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow.BreakKeyword; +import com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow.ContinueKeyword; +import com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow.FailKeyword; +import com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow.ReturnKeyword; +import com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike.ForKeyword; +import com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike.IfKeyword; +import com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike.WhileKeyword; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.BooleanAndOperation; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.BooleanNotOperation; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.BooleanOrOperation; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.ConcatenationOperation; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.DivisionOperation; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.ModuloOperation; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.MultiplicationOperation; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.NegationOperation; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.NumberAdditionOperation; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.SubtractionOperation; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.statements.EqualsStatement; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.statements.GreaterOrEqualsThanStatement; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.statements.GreaterThanStatement; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.statements.LessThanOrEqualsStatement; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.statements.LessThanStatement; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.statements.NotEqualsStatement; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Assignment; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Declaration; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Getter; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.addons.terrascript.tokenizer.Token; +import com.dfsek.terra.addons.terrascript.tokenizer.Tokenizer; + + +@SuppressWarnings("unchecked") +public class Parser { + private final String data; + private final Map>> functions = new HashMap<>(); + private final List ignoredFunctions = new ArrayList<>(); + + public Parser(String data) { + this.data = data; + } + + public Parser registerFunction(String name, FunctionBuilder> functionBuilder) { + functions.put(name, functionBuilder); + return this; + } + + public Parser ignoreFunction(String name) { + ignoredFunctions.add(name); + return this; + } + + /** + * Parse input + * + * @return executable {@link Block} + * + * @throws ParseException If parsing fails. + */ + public Block parse() { + return parseBlock(new Tokenizer(data), new HashMap<>(), false); + } + + private Keyword parseLoopLike(Tokenizer tokens, Map variableMap, boolean loop) throws ParseException { + + Token identifier = tokens.consume(); + ParserUtil.checkType(identifier, Token.Type.IF_STATEMENT, Token.Type.WHILE_LOOP, Token.Type.FOR_LOOP); + + ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_BEGIN); + + return switch(identifier.getType()) { + case FOR_LOOP -> parseForLoop(tokens, variableMap, identifier.getPosition()); + case IF_STATEMENT -> parseIfStatement(tokens, variableMap, identifier.getPosition(), loop); + case WHILE_LOOP -> parseWhileLoop(tokens, variableMap, identifier.getPosition()); + default -> throw new UnsupportedOperationException( + "Unknown keyword " + identifier.getContent() + ": " + identifier.getPosition()); + }; + } + + private WhileKeyword parseWhileLoop(Tokenizer tokens, Map variableMap, Position start) { + Returnable first = parseExpression(tokens, true, variableMap); + ParserUtil.checkReturnType(first, Returnable.ReturnType.BOOLEAN); + + ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_END); + + return new WhileKeyword(parseStatementBlock(tokens, variableMap, true), (Returnable) first, start); // While loop + } + + private IfKeyword parseIfStatement(Tokenizer tokens, Map variableMap, Position start, boolean loop) { + Returnable condition = parseExpression(tokens, true, variableMap); + ParserUtil.checkReturnType(condition, Returnable.ReturnType.BOOLEAN); + + ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_END); + + Block elseBlock = null; + Block statement = parseStatementBlock(tokens, variableMap, loop); + + List, Block>> elseIf = new ArrayList<>(); + + while(tokens.hasNext() && tokens.get().getType().equals(Token.Type.ELSE)) { + tokens.consume(); // Consume else. + if(tokens.get().getType().equals(Token.Type.IF_STATEMENT)) { + tokens.consume(); // Consume if. + Returnable elseCondition = parseExpression(tokens, true, variableMap); + ParserUtil.checkReturnType(elseCondition, Returnable.ReturnType.BOOLEAN); + elseIf.add(new IfKeyword.Pair<>((Returnable) elseCondition, parseStatementBlock(tokens, variableMap, loop))); + } else { + elseBlock = parseStatementBlock(tokens, variableMap, loop); + break; // Else must be last. + } + } + + return new IfKeyword(statement, (Returnable) condition, elseIf, elseBlock, start); // If statement + } + + private Block parseStatementBlock(Tokenizer tokens, Map variableMap, boolean loop) { + + if(tokens.get().getType().equals(Token.Type.BLOCK_BEGIN)) { + ParserUtil.checkType(tokens.consume(), Token.Type.BLOCK_BEGIN); + Block block = parseBlock(tokens, variableMap, loop); + ParserUtil.checkType(tokens.consume(), Token.Type.BLOCK_END); + return block; + } else { + Position position = tokens.get().getPosition(); + Block block = new Block(Collections.singletonList(parseItem(tokens, variableMap, loop)), position); + ParserUtil.checkType(tokens.consume(), Token.Type.STATEMENT_END); + return block; + } + } + + private ForKeyword parseForLoop(Tokenizer tokens, Map old, Position start) { + Map variableMap = new HashMap<>(old); // New scope + Token f = tokens.get(); + ParserUtil.checkType(f, Token.Type.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, Token.Type.IDENTIFIER); + Item initializer; + if(f.isVariableDeclaration()) { + Declaration forVar = parseVariableDeclaration(tokens, variableMap); + Token name = tokens.get(); + if(functions.containsKey(name.getContent()) || variableMap.containsKey(name.getContent())) + throw new ParseException(name.getContent() + " is already defined in this scope", name.getPosition()); + initializer = forVar; + } else initializer = parseExpression(tokens, true, variableMap); + ParserUtil.checkType(tokens.consume(), Token.Type.STATEMENT_END); + Returnable conditional = parseExpression(tokens, true, variableMap); + ParserUtil.checkReturnType(conditional, Returnable.ReturnType.BOOLEAN); + ParserUtil.checkType(tokens.consume(), Token.Type.STATEMENT_END); + + Item incrementer; + Token token = tokens.get(); + if(variableMap.containsKey(token.getContent())) { // Assume variable assignment + incrementer = parseAssignment(tokens, variableMap); + } else incrementer = parseFunction(tokens, true, variableMap); + + ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_END); + + return new ForKeyword(parseStatementBlock(tokens, variableMap, true), initializer, (Returnable) conditional, incrementer, + start); + } + + private Returnable parseExpression(Tokenizer tokens, boolean full, Map variableMap) { + boolean booleanInverted = false; // Check for boolean not operator + boolean negate = false; + if(tokens.get().getType().equals(Token.Type.BOOLEAN_NOT)) { + booleanInverted = true; + tokens.consume(); + } else if(tokens.get().getType().equals(Token.Type.SUBTRACTION_OPERATOR)) { + negate = true; + tokens.consume(); + } + + Token id = tokens.get(); + + ParserUtil.checkType(id, Token.Type.IDENTIFIER, Token.Type.BOOLEAN, Token.Type.STRING, Token.Type.NUMBER, Token.Type.GROUP_BEGIN); + + Returnable expression; + if(id.isConstant()) { + expression = parseConstantExpression(tokens); + } else if(id.getType().equals(Token.Type.GROUP_BEGIN)) { // Parse grouped expression + expression = parseGroup(tokens, variableMap); + } else { + if(functions.containsKey(id.getContent())) + expression = parseFunction(tokens, false, variableMap); + else if(variableMap.containsKey(id.getContent())) { + ParserUtil.checkType(tokens.consume(), Token.Type.IDENTIFIER); + expression = new Getter(id.getContent(), id.getPosition(), variableMap.get(id.getContent())); + } else throw new ParseException("Unexpected token \" " + id.getContent() + "\"", id.getPosition()); + } + + if(booleanInverted) { // Invert operation if boolean not detected + ParserUtil.checkReturnType(expression, Returnable.ReturnType.BOOLEAN); + expression = new BooleanNotOperation((Returnable) expression, expression.getPosition()); + } else if(negate) { + ParserUtil.checkReturnType(expression, Returnable.ReturnType.NUMBER); + expression = new NegationOperation((Returnable) expression, expression.getPosition()); + } + + if(full && tokens.get().isBinaryOperator()) { // Parse binary operations + return parseBinaryOperation(expression, tokens, variableMap); + } + return expression; + } + + private ConstantExpression parseConstantExpression(Tokenizer tokens) { + Token constantToken = tokens.consume(); + Position position = constantToken.getPosition(); + switch(constantToken.getType()) { + case NUMBER: + String content = constantToken.getContent(); + return new NumericConstant(content.contains(".") ? Double.parseDouble(content) : Integer.parseInt(content), position); + case STRING: + return new StringConstant(constantToken.getContent(), position); + case BOOLEAN: + return new BooleanConstant(Boolean.parseBoolean(constantToken.getContent()), position); + default: + throw new UnsupportedOperationException( + "Unsupported constant token: " + constantToken.getType() + " at position: " + position); + } + } + + private Returnable parseGroup(Tokenizer tokens, Map variableMap) { + ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_BEGIN); + Returnable expression = parseExpression(tokens, true, variableMap); // Parse inside of group as a separate expression + ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_END); + return expression; + } + + private BinaryOperation parseBinaryOperation(Returnable left, Tokenizer tokens, Map variableMap) { + Token binaryOperator = tokens.consume(); + ParserUtil.checkBinaryOperator(binaryOperator); + + Returnable right = parseExpression(tokens, false, variableMap); + + Token other = tokens.get(); + if(ParserUtil.hasPrecedence(binaryOperator.getType(), other.getType())) { + return assemble(left, parseBinaryOperation(right, tokens, variableMap), binaryOperator); + } else if(other.isBinaryOperator()) { + return parseBinaryOperation(assemble(left, right, binaryOperator), tokens, variableMap); + } + return assemble(left, right, binaryOperator); + } + + private BinaryOperation assemble(Returnable left, Returnable right, Token binaryOperator) { + if(binaryOperator.isStrictNumericOperator()) + ParserUtil.checkArithmeticOperation(left, right, binaryOperator); // Numeric type checking + if(binaryOperator.isStrictBooleanOperator()) ParserUtil.checkBooleanOperation(left, right, binaryOperator); // Boolean type checking + switch(binaryOperator.getType()) { + case ADDITION_OPERATOR: + if(left.returnType().equals(Returnable.ReturnType.NUMBER) && right.returnType().equals(Returnable.ReturnType.NUMBER)) { + return new NumberAdditionOperation((Returnable) left, (Returnable) right, binaryOperator.getPosition()); + } + return new ConcatenationOperation((Returnable) left, (Returnable) right, binaryOperator.getPosition()); + case SUBTRACTION_OPERATOR: + return new SubtractionOperation((Returnable) left, (Returnable) right, binaryOperator.getPosition()); + case MULTIPLICATION_OPERATOR: + return new MultiplicationOperation((Returnable) left, (Returnable) right, binaryOperator.getPosition()); + case DIVISION_OPERATOR: + return new DivisionOperation((Returnable) left, (Returnable) right, binaryOperator.getPosition()); + case EQUALS_OPERATOR: + return new EqualsStatement((Returnable) left, (Returnable) right, binaryOperator.getPosition()); + case NOT_EQUALS_OPERATOR: + return new NotEqualsStatement((Returnable) left, (Returnable) right, binaryOperator.getPosition()); + case GREATER_THAN_OPERATOR: + return new GreaterThanStatement((Returnable) left, (Returnable) right, binaryOperator.getPosition()); + case LESS_THAN_OPERATOR: + return new LessThanStatement((Returnable) left, (Returnable) right, binaryOperator.getPosition()); + case GREATER_THAN_OR_EQUALS_OPERATOR: + return new GreaterOrEqualsThanStatement((Returnable) left, (Returnable) right, + binaryOperator.getPosition()); + case LESS_THAN_OR_EQUALS_OPERATOR: + return new LessThanOrEqualsStatement((Returnable) left, (Returnable) right, binaryOperator.getPosition()); + case BOOLEAN_AND: + return new BooleanAndOperation((Returnable) left, (Returnable) right, binaryOperator.getPosition()); + case BOOLEAN_OR: + return new BooleanOrOperation((Returnable) left, (Returnable) right, binaryOperator.getPosition()); + case MODULO_OPERATOR: + return new ModuloOperation((Returnable) left, (Returnable) right, binaryOperator.getPosition()); + default: + throw new UnsupportedOperationException("Unsupported binary operator: " + binaryOperator.getType()); + } + } + + private Declaration parseVariableDeclaration(Tokenizer tokens, Map variableMap) { + Token type = tokens.consume(); + ParserUtil.checkType(type, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, Token.Type.NUMBER_VARIABLE); + + Returnable.ReturnType returnType = ParserUtil.getVariableReturnType(type); + + ParserUtil.checkVarType(type, returnType); // Check for type mismatch + Token identifier = tokens.consume(); + ParserUtil.checkType(identifier, Token.Type.IDENTIFIER); + if(functions.containsKey(identifier.getContent()) || variableMap.containsKey(identifier.getContent())) + throw new ParseException(identifier.getContent() + " is already defined in this scope", identifier.getPosition()); + ParserUtil.checkType(tokens.consume(), Token.Type.ASSIGNMENT); + + Returnable value = parseExpression(tokens, true, variableMap); + ParserUtil.checkReturnType(value, returnType); + + variableMap.put(identifier.getContent(), returnType); + + return new Declaration<>(tokens.get().getPosition(), identifier.getContent(), value, returnType); + } + + private Block parseBlock(Tokenizer tokens, Map superVars, boolean loop) { + List> parsedItems = new ArrayList<>(); + + Map parsedVariables = new HashMap<>( + superVars); // New hashmap as to not mutate parent scope's declarations. + + Token first = tokens.get(); + + while(tokens.hasNext()) { + Token token = tokens.get(); + if(token.getType().equals(Token.Type.BLOCK_END)) break; // Stop parsing at block end. + Item parsedItem = parseItem(tokens, parsedVariables, loop); + if(parsedItem != Function.NULL) { + parsedItems.add(parsedItem); + } + if(tokens.hasNext() && !token.isLoopLike()) ParserUtil.checkType(tokens.consume(), Token.Type.STATEMENT_END); + } + return new Block(parsedItems, first.getPosition()); + } + + private Item parseItem(Tokenizer tokens, Map variableMap, boolean loop) { + Token token = tokens.get(); + if(loop) ParserUtil.checkType(token, Token.Type.IDENTIFIER, Token.Type.IF_STATEMENT, Token.Type.WHILE_LOOP, Token.Type.FOR_LOOP, + Token.Type.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, + Token.Type.RETURN, Token.Type.BREAK, Token.Type.CONTINUE, Token.Type.FAIL); + else ParserUtil.checkType(token, Token.Type.IDENTIFIER, Token.Type.IF_STATEMENT, Token.Type.WHILE_LOOP, Token.Type.FOR_LOOP, + Token.Type.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, Token.Type.RETURN, + Token.Type.FAIL); + + if(token.isLoopLike()) { // Parse loop-like tokens (if, while, etc) + return parseLoopLike(tokens, variableMap, loop); + } else if(token.isIdentifier()) { // Parse identifiers + if(variableMap.containsKey(token.getContent())) { // Assume variable assignment + return parseAssignment(tokens, variableMap); + } else return parseFunction(tokens, true, variableMap); + } else if(token.isVariableDeclaration()) { + + return parseVariableDeclaration(tokens, variableMap); + + } else if(token.getType().equals(Token.Type.RETURN)) return new ReturnKeyword(tokens.consume().getPosition()); + else if(token.getType().equals(Token.Type.BREAK)) return new BreakKeyword(tokens.consume().getPosition()); + else if(token.getType().equals(Token.Type.CONTINUE)) return new ContinueKeyword(tokens.consume().getPosition()); + else if(token.getType().equals(Token.Type.FAIL)) return new FailKeyword(tokens.consume().getPosition()); + else throw new UnsupportedOperationException("Unexpected token " + token.getType() + ": " + token.getPosition()); + } + + private Assignment parseAssignment(Tokenizer tokens, Map variableMap) { + Token identifier = tokens.consume(); + + ParserUtil.checkType(identifier, Token.Type.IDENTIFIER); + + ParserUtil.checkType(tokens.consume(), Token.Type.ASSIGNMENT); + + Returnable value = parseExpression(tokens, true, variableMap); + + ParserUtil.checkReturnType(value, variableMap.get(identifier.getContent())); + + return new Assignment<>(value, identifier.getContent(), identifier.getPosition()); + } + + private Function parseFunction(Tokenizer tokens, boolean fullStatement, Map variableMap) { + Token identifier = tokens.consume(); + ParserUtil.checkType(identifier, Token.Type.IDENTIFIER); // First token must be identifier + + if(!functions.containsKey(identifier.getContent())) + throw new ParseException("No such function \"" + identifier.getContent() + "\"", identifier.getPosition()); + + ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_BEGIN); // Second is body begin + + + List> args = getArgs(tokens, variableMap); // Extract arguments, consume the rest. + + ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_END); // Remove body end + + if(fullStatement) ParserUtil.checkType(tokens.get(), Token.Type.STATEMENT_END); + + if(ignoredFunctions.contains(identifier.getContent())) { + return Function.NULL; + } + + if(functions.containsKey(identifier.getContent())) { + FunctionBuilder builder = functions.get(identifier.getContent()); + + if(builder.argNumber() != -1 && args.size() != builder.argNumber()) + throw new ParseException("Expected " + builder.argNumber() + " arguments, found " + args.size(), identifier.getPosition()); + + for(int i = 0; i < args.size(); i++) { + Returnable argument = args.get(i); + if(builder.getArgument(i) == null) + throw new ParseException("Unexpected argument at position " + i + " in function " + identifier.getContent(), + identifier.getPosition()); + ParserUtil.checkReturnType(argument, builder.getArgument(i)); + } + return builder.build(args, identifier.getPosition()); + } + throw new UnsupportedOperationException("Unsupported function: " + identifier.getContent()); + } + + private List> getArgs(Tokenizer tokens, Map variableMap) { + List> args = new ArrayList<>(); + + while(!tokens.get().getType().equals(Token.Type.GROUP_END)) { + args.add(parseExpression(tokens, true, variableMap)); + ParserUtil.checkType(tokens.get(), Token.Type.SEPARATOR, Token.Type.GROUP_END); + if(tokens.get().getType().equals(Token.Type.SEPARATOR)) tokens.consume(); + } + return args; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/ParserUtil.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/ParserUtil.java new file mode 100644 index 000000000..3224c69ad --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/ParserUtil.java @@ -0,0 +1,108 @@ +package com.dfsek.terra.addons.terrascript.parser; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Token; + + +public class ParserUtil { + + private static final Map> PRECEDENCE = new HashMap<>(); // If second has precedence, true. + private static final List ARITHMETIC = Arrays.asList(Token.Type.ADDITION_OPERATOR, Token.Type.SUBTRACTION_OPERATOR, + Token.Type.MULTIPLICATION_OPERATOR, Token.Type.DIVISION_OPERATOR, + Token.Type.MODULO_OPERATOR); + private static final List COMPARISON = Arrays.asList(Token.Type.EQUALS_OPERATOR, Token.Type.NOT_EQUALS_OPERATOR, + Token.Type.LESS_THAN_OPERATOR, Token.Type.LESS_THAN_OR_EQUALS_OPERATOR, + Token.Type.GREATER_THAN_OPERATOR, + Token.Type.GREATER_THAN_OR_EQUALS_OPERATOR); + + static { // Setup precedence + Map add = new HashMap<>(); // Addition/subtraction before Multiplication/division. + add.put(Token.Type.MULTIPLICATION_OPERATOR, true); + add.put(Token.Type.DIVISION_OPERATOR, true); + + PRECEDENCE.put(Token.Type.ADDITION_OPERATOR, add); + PRECEDENCE.put(Token.Type.SUBTRACTION_OPERATOR, add); + + Map numericBoolean = new HashMap<>(); + + ARITHMETIC.forEach(op -> numericBoolean.put(op, true)); // Numbers before comparison + COMPARISON.forEach(op -> PRECEDENCE.put(op, numericBoolean)); + + + Map booleanOps = new HashMap<>(); + ARITHMETIC.forEach(op -> booleanOps.put(op, true)); // Everything before boolean + COMPARISON.forEach(op -> booleanOps.put(op, true)); + + + PRECEDENCE.put(Token.Type.BOOLEAN_AND, booleanOps); + PRECEDENCE.put(Token.Type.BOOLEAN_OR, booleanOps); + } + + public static void checkType(Token token, Token.Type... expected) { + for(Token.Type type : expected) if(token.getType().equals(type)) return; + throw new ParseException("Expected " + Arrays.toString(expected) + " but found " + token.getType(), token.getPosition()); + } + + public static void checkReturnType(Returnable returnable, Returnable.ReturnType... types) { + for(Returnable.ReturnType type : types) if(returnable.returnType().equals(type)) return; + throw new ParseException("Expected " + Arrays.toString(types) + " but found " + returnable.returnType(), returnable.getPosition()); + } + + public static void checkArithmeticOperation(Returnable left, Returnable right, Token operation) { + if(!left.returnType().equals(Returnable.ReturnType.NUMBER) || !right.returnType().equals(Returnable.ReturnType.NUMBER)) { + throw new ParseException( + "Operation " + operation.getType() + " not supported between " + left.returnType() + " and " + right.returnType(), + operation.getPosition()); + } + } + + public static void checkBooleanOperation(Returnable left, Returnable right, Token operation) { + if(!left.returnType().equals(Returnable.ReturnType.BOOLEAN) || !right.returnType().equals(Returnable.ReturnType.BOOLEAN)) { + throw new ParseException( + "Operation " + operation.getType() + " not supported between " + left.returnType() + " and " + right.returnType(), + operation.getPosition()); + } + } + + public static void checkVarType(Token token, Returnable.ReturnType returnType) { + if(returnType.equals(Returnable.ReturnType.STRING) && token.getType().equals(Token.Type.STRING_VARIABLE)) return; + if(returnType.equals(Returnable.ReturnType.NUMBER) && token.getType().equals(Token.Type.NUMBER_VARIABLE)) return; + if(returnType.equals(Returnable.ReturnType.BOOLEAN) && token.getType().equals(Token.Type.BOOLEAN_VARIABLE)) return; + throw new ParseException("Type mismatch, cannot convert from " + returnType + " to " + token.getType(), token.getPosition()); + } + + /** + * Checks if token is a binary operator + * + * @param token Token to check + * + * @throws ParseException If token isn't a binary operator + */ + public static void checkBinaryOperator(Token token) { + if(!token.isBinaryOperator()) + throw new ParseException("Expected binary operator, found " + token.getType(), token.getPosition()); + } + + public static Returnable.ReturnType getVariableReturnType(Token varToken) { + return switch(varToken.getType()) { + case NUMBER_VARIABLE -> Returnable.ReturnType.NUMBER; + case STRING_VARIABLE -> Returnable.ReturnType.STRING; + case BOOLEAN_VARIABLE -> Returnable.ReturnType.BOOLEAN; + default -> throw new ParseException("Unexpected token " + varToken.getType() + "; expected variable declaration", + varToken.getPosition()); + }; + } + + public static boolean hasPrecedence(Token.Type first, Token.Type second) { + if(!PRECEDENCE.containsKey(first)) return false; + Map pre = PRECEDENCE.get(first); + if(!pre.containsKey(second)) return false; + return pre.get(second); + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/exceptions/ParseException.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/exceptions/ParseException.java new file mode 100644 index 000000000..b56340d0a --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/exceptions/ParseException.java @@ -0,0 +1,28 @@ +package com.dfsek.terra.addons.terrascript.parser.exceptions; + +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class ParseException extends RuntimeException { + private static final long serialVersionUID = 6744390543046766386L; + private final Position position; + + public ParseException(String message, Position position) { + super(message); + this.position = position; + } + + public ParseException(String message, Position position, Throwable cause) { + super(message, cause); + this.position = position; + } + + @Override + public String getMessage() { + return super.getMessage() + ": " + position; + } + + public Position getPosition() { + return position; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Block.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Block.java new file mode 100644 index 000000000..cf9b15c1c --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Block.java @@ -0,0 +1,82 @@ +package com.dfsek.terra.addons.terrascript.parser.lang; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class Block implements Item> { + private final List> items; + private final Position position; + + public Block(List> items, Position position) { + this.items = items; + this.position = position; + } + + public ReturnInfo apply(ImplementationArguments implementationArguments) { + return apply(implementationArguments, new HashMap<>()); + } + + @Override + public ReturnInfo apply(ImplementationArguments implementationArguments, Map> variableMap) { + Map> scope = new HashMap<>(variableMap); + for(Item item : items) { + Object result = item.apply(implementationArguments, scope); + if(result instanceof ReturnInfo) { + ReturnInfo level = (ReturnInfo) result; + if(!level.getLevel().equals(ReturnLevel.NONE)) return level; + } + } + return new ReturnInfo<>(ReturnLevel.NONE, null); + } + + @Override + public Position getPosition() { + return position; + } + + public List> getItems() { + return items; + } + + public enum ReturnLevel { + NONE(false), + BREAK(false), + CONTINUE(false), + RETURN(true), + FAIL(true); + + private final boolean returnFast; + + ReturnLevel(boolean returnFast) { + this.returnFast = returnFast; + } + + public boolean isReturnFast() { + return returnFast; + } + } + + + public static class ReturnInfo { + private final ReturnLevel level; + private final T data; + + public ReturnInfo(ReturnLevel level, T data) { + this.level = level; + this.data = data; + } + + public ReturnLevel getLevel() { + return level; + } + + public T getData() { + return data; + } + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/ImplementationArguments.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/ImplementationArguments.java new file mode 100644 index 000000000..1a5963d20 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/ImplementationArguments.java @@ -0,0 +1,7 @@ +package com.dfsek.terra.addons.terrascript.parser.lang; + +/** + * Arguments passed to {@link Item}s by the implementation + */ +public interface ImplementationArguments { +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Item.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Item.java new file mode 100644 index 000000000..07721b1d3 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Item.java @@ -0,0 +1,13 @@ +package com.dfsek.terra.addons.terrascript.parser.lang; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public interface Item { + T apply(ImplementationArguments implementationArguments, Map> variableMap); + + Position getPosition(); +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Keyword.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Keyword.java new file mode 100644 index 000000000..5a76a66bd --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Keyword.java @@ -0,0 +1,4 @@ +package com.dfsek.terra.addons.terrascript.parser.lang; + +public interface Keyword extends Returnable { +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Returnable.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Returnable.java new file mode 100644 index 000000000..a1f082985 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Returnable.java @@ -0,0 +1,23 @@ +package com.dfsek.terra.addons.terrascript.parser.lang; + +public interface Returnable extends Item { + ReturnType returnType(); + + enum ReturnType { + NUMBER(true), + STRING(true), + BOOLEAN(false), + VOID(false), + OBJECT(false); + + private final boolean comparable; + + ReturnType(boolean comparable) { + this.comparable = comparable; + } + + public boolean isComparable() { + return comparable; + } + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Statement.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Statement.java new file mode 100644 index 000000000..c0030a0ed --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/Statement.java @@ -0,0 +1,4 @@ +package com.dfsek.terra.addons.terrascript.parser.lang; + +public interface Statement extends Item { +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/constants/BooleanConstant.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/constants/BooleanConstant.java new file mode 100644 index 000000000..be22b5641 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/constants/BooleanConstant.java @@ -0,0 +1,15 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.constants; + +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class BooleanConstant extends ConstantExpression { + public BooleanConstant(Boolean constant, Position position) { + super(constant, position); + } + + @Override + public ReturnType returnType() { + return ReturnType.BOOLEAN; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/constants/ConstantExpression.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/constants/ConstantExpression.java new file mode 100644 index 000000000..4b5a4ecad --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/constants/ConstantExpression.java @@ -0,0 +1,33 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.constants; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public abstract class ConstantExpression implements Returnable { + private final T constant; + private final Position position; + + public ConstantExpression(T constant, Position position) { + this.constant = constant; + this.position = position; + } + + @Override + public T apply(ImplementationArguments implementationArguments, Map> variableMap) { + return constant; + } + + @Override + public Position getPosition() { + return position; + } + + public T getConstant() { + return constant; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/constants/NumericConstant.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/constants/NumericConstant.java new file mode 100644 index 000000000..568668671 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/constants/NumericConstant.java @@ -0,0 +1,16 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.constants; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class NumericConstant extends ConstantExpression { + public NumericConstant(Number constant, Position position) { + super(constant, position); + } + + @Override + public Returnable.ReturnType returnType() { + return Returnable.ReturnType.NUMBER; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/constants/StringConstant.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/constants/StringConstant.java new file mode 100644 index 000000000..7e6bc04aa --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/constants/StringConstant.java @@ -0,0 +1,16 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.constants; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class StringConstant extends ConstantExpression { + public StringConstant(String constant, Position position) { + super(constant, position); + } + + @Override + public Returnable.ReturnType returnType() { + return Returnable.ReturnType.STRING; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/Function.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/Function.java new file mode 100644 index 000000000..2040df1ac --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/Function.java @@ -0,0 +1,28 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.functions; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public interface Function extends Returnable { + Function NULL = new Function<>() { + @Override + public ReturnType returnType() { + return null; + } + + @Override + public Object apply(ImplementationArguments implementationArguments, Map> variableMap) { + return null; + } + + @Override + public Position getPosition() { + return null; + } + }; +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/FunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/FunctionBuilder.java new file mode 100644 index 000000000..a2916174b --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/FunctionBuilder.java @@ -0,0 +1,16 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.functions; + +import java.util.List; + +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public interface FunctionBuilder> { + T build(List> argumentList, Position position); + + int argNumber(); + + Returnable.ReturnType getArgument(int position); +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/def/FunctionBlock.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/def/FunctionBlock.java new file mode 100644 index 000000000..1e21175ab --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/functions/def/FunctionBlock.java @@ -0,0 +1,47 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.functions.def; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Item; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class FunctionBlock implements Item { + private final List> items; + private final Position position; + private final T defaultVal; + + public FunctionBlock(List> items, T defaultVal, Position position) { + this.items = items; + this.position = position; + this.defaultVal = defaultVal; + } + + @SuppressWarnings("unchecked") + @Override + public synchronized T apply(ImplementationArguments implementationArguments, Map> variableMap) { + Map> scope = new HashMap<>(variableMap); + for(Item item : items) { + Object result = item.apply(implementationArguments, variableMap); + if(result instanceof Block.ReturnInfo) { + Block.ReturnInfo level = (Block.ReturnInfo) result; + if(level.getLevel().equals(Block.ReturnLevel.RETURN)) return level.getData(); + } + } + return defaultVal; + } + + @Override + public Position getPosition() { + return position; + } + + public List> getItems() { + return items; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/BreakKeyword.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/BreakKeyword.java new file mode 100644 index 000000000..fe44e8d20 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/BreakKeyword.java @@ -0,0 +1,33 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Keyword; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class BreakKeyword implements Keyword> { + private final Position position; + + public BreakKeyword(Position position) { + this.position = position; + } + + @Override + public Block.ReturnInfo apply(ImplementationArguments implementationArguments, Map> variableMap) { + return new Block.ReturnInfo<>(Block.ReturnLevel.BREAK, null); + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/ContinueKeyword.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/ContinueKeyword.java new file mode 100644 index 000000000..352529f61 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/ContinueKeyword.java @@ -0,0 +1,33 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Keyword; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class ContinueKeyword implements Keyword> { + private final Position position; + + public ContinueKeyword(Position position) { + this.position = position; + } + + @Override + public Block.ReturnInfo apply(ImplementationArguments implementationArguments, Map> variableMap) { + return new Block.ReturnInfo<>(Block.ReturnLevel.CONTINUE, null); + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/FailKeyword.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/FailKeyword.java new file mode 100644 index 000000000..2d0ea7247 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/FailKeyword.java @@ -0,0 +1,33 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Keyword; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class FailKeyword implements Keyword> { + private final Position position; + + public FailKeyword(Position position) { + this.position = position; + } + + @Override + public Block.ReturnInfo apply(ImplementationArguments implementationArguments, Map> variableMap) { + return new Block.ReturnInfo<>(Block.ReturnLevel.FAIL, null); + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/ReturnKeyword.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/ReturnKeyword.java new file mode 100644 index 000000000..2130fc1e9 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/flow/ReturnKeyword.java @@ -0,0 +1,33 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Keyword; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class ReturnKeyword implements Keyword> { + private final Position position; + + public ReturnKeyword(Position position) { + this.position = position; + } + + @Override + public Block.ReturnInfo apply(ImplementationArguments implementationArguments, Map> variableMap) { + return new Block.ReturnInfo<>(Block.ReturnLevel.RETURN, null); + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/ForKeyword.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/ForKeyword.java new file mode 100644 index 000000000..045431df7 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/ForKeyword.java @@ -0,0 +1,50 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Item; +import com.dfsek.terra.addons.terrascript.parser.lang.Keyword; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class ForKeyword implements Keyword> { + private final Block conditional; + private final Item initializer; + private final Returnable statement; + private final Item incrementer; + private final Position position; + + public ForKeyword(Block conditional, Item initializer, Returnable statement, Item incrementer, Position position) { + this.conditional = conditional; + this.initializer = initializer; + this.statement = statement; + this.incrementer = incrementer; + this.position = position; + } + + @Override + public Block.ReturnInfo apply(ImplementationArguments implementationArguments, Map> variableMap) { + for(initializer.apply(implementationArguments, variableMap); + statement.apply(implementationArguments, variableMap); + incrementer.apply(implementationArguments, variableMap)) { + Block.ReturnInfo level = conditional.apply(implementationArguments, variableMap); + if(level.getLevel().equals(Block.ReturnLevel.BREAK)) break; + if(level.getLevel().isReturnFast()) return level; + } + return new Block.ReturnInfo<>(Block.ReturnLevel.NONE, null); + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/IfKeyword.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/IfKeyword.java new file mode 100644 index 000000000..bd6a5549d --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/IfKeyword.java @@ -0,0 +1,74 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike; + +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Keyword; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class IfKeyword implements Keyword> { + private final Block conditional; + private final Returnable statement; + private final Position position; + private final List, Block>> elseIf; + private final Block elseBlock; + + public IfKeyword(Block conditional, Returnable statement, List, Block>> elseIf, + @Nullable Block elseBlock, Position position) { + this.conditional = conditional; + this.statement = statement; + this.position = position; + this.elseIf = elseIf; + this.elseBlock = elseBlock; + } + + @Override + public Block.ReturnInfo apply(ImplementationArguments implementationArguments, Map> variableMap) { + if(statement.apply(implementationArguments, variableMap)) return conditional.apply(implementationArguments, variableMap); + else { + for(Pair, Block> pair : elseIf) { + if(pair.getLeft().apply(implementationArguments, variableMap)) { + return pair.getRight().apply(implementationArguments, variableMap); + } + } + if(elseBlock != null) return elseBlock.apply(implementationArguments, variableMap); + } + return new Block.ReturnInfo<>(Block.ReturnLevel.NONE, null); + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } + + + public static class Pair { + private final L left; + private final R right; + + public Pair(L left, R right) { + this.left = left; + this.right = right; + } + + public L getLeft() { + return left; + } + + public R getRight() { + return right; + } + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/WhileKeyword.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/WhileKeyword.java new file mode 100644 index 000000000..49457f5b9 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/keywords/looplike/WhileKeyword.java @@ -0,0 +1,43 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Keyword; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class WhileKeyword implements Keyword> { + private final Block conditional; + private final Returnable statement; + private final Position position; + + public WhileKeyword(Block conditional, Returnable statement, Position position) { + this.conditional = conditional; + this.statement = statement; + this.position = position; + } + + @Override + public Block.ReturnInfo apply(ImplementationArguments implementationArguments, Map> variableMap) { + while(statement.apply(implementationArguments, variableMap)) { + Block.ReturnInfo level = conditional.apply(implementationArguments, variableMap); + if(level.getLevel().equals(Block.ReturnLevel.BREAK)) break; + if(level.getLevel().isReturnFast()) return level; + } + return new Block.ReturnInfo<>(Block.ReturnLevel.NONE, null); + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/BinaryOperation.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/BinaryOperation.java new file mode 100644 index 000000000..aa9fa7707 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/BinaryOperation.java @@ -0,0 +1,33 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public abstract class BinaryOperation implements Returnable { + private final Returnable left; + private final Returnable right; + private final Position start; + + public BinaryOperation(Returnable left, Returnable right, Position start) { + this.left = left; + this.right = right; + this.start = start; + } + + public abstract O apply(I left, I right); + + @Override + public O apply(ImplementationArguments implementationArguments, Map> variableMap) { + return apply(left.apply(implementationArguments, variableMap), right.apply(implementationArguments, variableMap)); + } + + @Override + public Position getPosition() { + return start; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/BooleanAndOperation.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/BooleanAndOperation.java new file mode 100644 index 000000000..90380e7a2 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/BooleanAndOperation.java @@ -0,0 +1,21 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class BooleanAndOperation extends BinaryOperation { + public BooleanAndOperation(Returnable left, Returnable right, Position start) { + super(left, right, start); + } + + @Override + public Boolean apply(Boolean left, Boolean right) { + return left && right; + } + + @Override + public ReturnType returnType() { + return ReturnType.BOOLEAN; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/BooleanNotOperation.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/BooleanNotOperation.java new file mode 100644 index 000000000..3b2ccea7f --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/BooleanNotOperation.java @@ -0,0 +1,21 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class BooleanNotOperation extends UnaryOperation { + public BooleanNotOperation(Returnable input, Position position) { + super(input, position); + } + + @Override + public Boolean apply(Boolean input) { + return !input; + } + + @Override + public ReturnType returnType() { + return ReturnType.BOOLEAN; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/BooleanOrOperation.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/BooleanOrOperation.java new file mode 100644 index 000000000..5322a0708 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/BooleanOrOperation.java @@ -0,0 +1,21 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class BooleanOrOperation extends BinaryOperation { + public BooleanOrOperation(Returnable left, Returnable right, Position start) { + super(left, right, start); + } + + @Override + public Boolean apply(Boolean left, Boolean right) { + return left || right; + } + + @Override + public ReturnType returnType() { + return ReturnType.BOOLEAN; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/ConcatenationOperation.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/ConcatenationOperation.java new file mode 100644 index 000000000..46ad64a10 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/ConcatenationOperation.java @@ -0,0 +1,21 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class ConcatenationOperation extends BinaryOperation { + public ConcatenationOperation(Returnable left, Returnable right, Position position) { + super(left, right, position); + } + + @Override + public String apply(Object left, Object right) { + return left.toString() + right.toString(); + } + + @Override + public Returnable.ReturnType returnType() { + return Returnable.ReturnType.STRING; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/DivisionOperation.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/DivisionOperation.java new file mode 100644 index 000000000..1610502dc --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/DivisionOperation.java @@ -0,0 +1,21 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class DivisionOperation extends BinaryOperation { + public DivisionOperation(Returnable left, Returnable right, Position position) { + super(left, right, position); + } + + @Override + public Number apply(Number left, Number right) { + return left.doubleValue() / right.doubleValue(); + } + + @Override + public Returnable.ReturnType returnType() { + return Returnable.ReturnType.NUMBER; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/ModuloOperation.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/ModuloOperation.java new file mode 100644 index 000000000..549962922 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/ModuloOperation.java @@ -0,0 +1,21 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class ModuloOperation extends BinaryOperation { + public ModuloOperation(Returnable left, Returnable right, Position start) { + super(left, right, start); + } + + @Override + public Number apply(Number left, Number right) { + return left.doubleValue() % right.doubleValue(); + } + + @Override + public ReturnType returnType() { + return ReturnType.NUMBER; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/MultiplicationOperation.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/MultiplicationOperation.java new file mode 100644 index 000000000..f3fef308b --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/MultiplicationOperation.java @@ -0,0 +1,21 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class MultiplicationOperation extends BinaryOperation { + public MultiplicationOperation(Returnable left, Returnable right, Position position) { + super(left, right, position); + } + + @Override + public Number apply(Number left, Number right) { + return left.doubleValue() * right.doubleValue(); + } + + @Override + public ReturnType returnType() { + return ReturnType.NUMBER; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/NegationOperation.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/NegationOperation.java new file mode 100644 index 000000000..640b53443 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/NegationOperation.java @@ -0,0 +1,21 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class NegationOperation extends UnaryOperation { + public NegationOperation(Returnable input, Position position) { + super(input, position); + } + + @Override + public Number apply(Number input) { + return -input.doubleValue(); + } + + @Override + public ReturnType returnType() { + return ReturnType.NUMBER; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/NumberAdditionOperation.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/NumberAdditionOperation.java new file mode 100644 index 000000000..2b93c4da8 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/NumberAdditionOperation.java @@ -0,0 +1,21 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class NumberAdditionOperation extends BinaryOperation { + public NumberAdditionOperation(Returnable left, Returnable right, Position position) { + super(left, right, position); + } + + @Override + public Number apply(Number left, Number right) { + return left.doubleValue() + right.doubleValue(); + } + + @Override + public ReturnType returnType() { + return ReturnType.NUMBER; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/SubtractionOperation.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/SubtractionOperation.java new file mode 100644 index 000000000..a81d70ad4 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/SubtractionOperation.java @@ -0,0 +1,21 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class SubtractionOperation extends BinaryOperation { + public SubtractionOperation(Returnable left, Returnable right, Position position) { + super(left, right, position); + } + + @Override + public Number apply(Number left, Number right) { + return left.doubleValue() - right.doubleValue(); + } + + @Override + public ReturnType returnType() { + return ReturnType.NUMBER; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/UnaryOperation.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/UnaryOperation.java new file mode 100644 index 000000000..3fbb194d0 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/UnaryOperation.java @@ -0,0 +1,31 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public abstract class UnaryOperation implements Returnable { + private final Returnable input; + private final Position position; + + public UnaryOperation(Returnable input, Position position) { + this.input = input; + this.position = position; + } + + public abstract T apply(T input); + + @Override + public T apply(ImplementationArguments implementationArguments, Map> variableMap) { + return apply(input.apply(implementationArguments, variableMap)); + } + + @Override + public Position getPosition() { + return position; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/EqualsStatement.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/EqualsStatement.java new file mode 100644 index 000000000..68906e9c8 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/EqualsStatement.java @@ -0,0 +1,31 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements; + +import net.jafama.FastMath; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class EqualsStatement extends BinaryOperation { + private static final double EPSILON = 0.000000001D; + + public EqualsStatement(Returnable left, Returnable right, Position position) { + super(left, right, position); + } + + @Override + public Boolean apply(Object left, Object right) { + if(left instanceof Number && right instanceof Number) { + return FastMath.abs(((Number) left).doubleValue() - ((Number) right).doubleValue()) <= EPSILON; + } + + return left.equals(right); + } + + + @Override + public Returnable.ReturnType returnType() { + return Returnable.ReturnType.BOOLEAN; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/GreaterOrEqualsThanStatement.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/GreaterOrEqualsThanStatement.java new file mode 100644 index 000000000..c26e423ee --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/GreaterOrEqualsThanStatement.java @@ -0,0 +1,23 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class GreaterOrEqualsThanStatement extends BinaryOperation { + public GreaterOrEqualsThanStatement(Returnable left, Returnable right, Position position) { + super(left, right, position); + } + + @Override + public Boolean apply(Number left, Number right) { + return left.doubleValue() >= right.doubleValue(); + } + + + @Override + public Returnable.ReturnType returnType() { + return Returnable.ReturnType.BOOLEAN; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/GreaterThanStatement.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/GreaterThanStatement.java new file mode 100644 index 000000000..be21852b4 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/GreaterThanStatement.java @@ -0,0 +1,23 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class GreaterThanStatement extends BinaryOperation { + public GreaterThanStatement(Returnable left, Returnable right, Position position) { + super(left, right, position); + } + + @Override + public Boolean apply(Number left, Number right) { + return left.doubleValue() > right.doubleValue(); + } + + + @Override + public Returnable.ReturnType returnType() { + return Returnable.ReturnType.BOOLEAN; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/LessThanOrEqualsStatement.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/LessThanOrEqualsStatement.java new file mode 100644 index 000000000..d44c51b00 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/LessThanOrEqualsStatement.java @@ -0,0 +1,23 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class LessThanOrEqualsStatement extends BinaryOperation { + public LessThanOrEqualsStatement(Returnable left, Returnable right, Position position) { + super(left, right, position); + } + + @Override + public Boolean apply(Number left, Number right) { + return left.doubleValue() <= right.doubleValue(); + } + + + @Override + public Returnable.ReturnType returnType() { + return Returnable.ReturnType.BOOLEAN; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/LessThanStatement.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/LessThanStatement.java new file mode 100644 index 000000000..639b45017 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/LessThanStatement.java @@ -0,0 +1,23 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class LessThanStatement extends BinaryOperation { + public LessThanStatement(Returnable left, Returnable right, Position position) { + super(left, right, position); + } + + @Override + public Boolean apply(Number left, Number right) { + return left.doubleValue() < right.doubleValue(); + } + + + @Override + public Returnable.ReturnType returnType() { + return Returnable.ReturnType.BOOLEAN; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/NotEqualsStatement.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/NotEqualsStatement.java new file mode 100644 index 000000000..8d39caf3e --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/operations/statements/NotEqualsStatement.java @@ -0,0 +1,23 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class NotEqualsStatement extends BinaryOperation { + public NotEqualsStatement(Returnable left, Returnable right, Position position) { + super(left, right, position); + } + + @Override + public Boolean apply(Object left, Object right) { + return !left.equals(right); + } + + + @Override + public Returnable.ReturnType returnType() { + return Returnable.ReturnType.BOOLEAN; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/Assignment.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/Assignment.java new file mode 100644 index 000000000..32be67aa9 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/Assignment.java @@ -0,0 +1,34 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.variables; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Item; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class Assignment implements Item { + private final Returnable value; + private final Position position; + private final String identifier; + + public Assignment(Returnable value, String identifier, Position position) { + this.value = value; + this.identifier = identifier; + this.position = position; + } + + @SuppressWarnings("unchecked") + @Override + public synchronized T apply(ImplementationArguments implementationArguments, Map> variableMap) { + T val = value.apply(implementationArguments, variableMap); + ((Variable) variableMap.get(identifier)).setValue(val); + return val; + } + + @Override + public Position getPosition() { + return position; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/BooleanVariable.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/BooleanVariable.java new file mode 100644 index 000000000..9e124bd58 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/BooleanVariable.java @@ -0,0 +1,35 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.variables; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class BooleanVariable implements Variable { + private final Position position; + private Boolean value; + + public BooleanVariable(Boolean value, Position position) { + this.value = value; + this.position = position; + } + + @Override + public Boolean getValue() { + return value; + } + + @Override + public void setValue(Boolean value) { + this.value = value; + } + + @Override + public Returnable.ReturnType getType() { + return Returnable.ReturnType.BOOLEAN; + } + + @Override + public Position getPosition() { + return position; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/Declaration.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/Declaration.java new file mode 100644 index 000000000..90b93f84b --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/Declaration.java @@ -0,0 +1,55 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.variables; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Item; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class Declaration implements Item { + private final Position position; + private final String identifier; + private final Returnable value; + private final Returnable.ReturnType type; + + public Declaration(Position position, String identifier, Returnable value, Returnable.ReturnType type) { + switch(type) { + case STRING: + case BOOLEAN: + case NUMBER: + break; + default: + throw new IllegalArgumentException("Invalid variable type: " + type); + } + this.position = position; + this.identifier = identifier; + this.value = value; + this.type = type; + } + + @Override + public T apply(ImplementationArguments implementationArguments, Map> variableMap) { + T result = value.apply(implementationArguments, variableMap); + switch(type) { + case NUMBER -> variableMap.put(identifier, new NumberVariable((Number) result, position)); + case BOOLEAN -> variableMap.put(identifier, new BooleanVariable((Boolean) result, position)); + case STRING -> variableMap.put(identifier, new StringVariable((String) result, position)); + } + return result; + } + + @Override + public Position getPosition() { + return position; + } + + public Returnable.ReturnType getType() { + return type; + } + + public String getIdentifier() { + return identifier; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/Getter.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/Getter.java new file mode 100644 index 000000000..c13fe536d --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/Getter.java @@ -0,0 +1,35 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.variables; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class Getter implements Returnable { + private final String identifier; + private final Position position; + private final ReturnType type; + + public Getter(String identifier, Position position, ReturnType type) { + this.identifier = identifier; + this.position = position; + this.type = type; + } + + @Override + public ReturnType returnType() { + return type; + } + + @Override + public synchronized Object apply(ImplementationArguments implementationArguments, Map> variableMap) { + return variableMap.get(identifier).getValue(); + } + + @Override + public Position getPosition() { + return position; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/NumberVariable.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/NumberVariable.java new file mode 100644 index 000000000..803e59a81 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/NumberVariable.java @@ -0,0 +1,35 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.variables; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class NumberVariable implements Variable { + private final Position position; + private Number value; + + public NumberVariable(Number value, Position position) { + this.value = value; + this.position = position; + } + + @Override + public Number getValue() { + return value; + } + + @Override + public void setValue(Number value) { + this.value = value; + } + + @Override + public Returnable.ReturnType getType() { + return Returnable.ReturnType.NUMBER; + } + + @Override + public Position getPosition() { + return position; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/StringVariable.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/StringVariable.java new file mode 100644 index 000000000..a4b104458 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/StringVariable.java @@ -0,0 +1,35 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.variables; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class StringVariable implements Variable { + private final Position position; + private String value; + + public StringVariable(String value, Position position) { + this.value = value; + this.position = position; + } + + @Override + public String getValue() { + return value; + } + + @Override + public void setValue(String value) { + this.value = value; + } + + @Override + public Returnable.ReturnType getType() { + return Returnable.ReturnType.STRING; + } + + @Override + public Position getPosition() { + return position; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/Variable.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/Variable.java new file mode 100644 index 000000000..cc4beb9de --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/parser/lang/variables/Variable.java @@ -0,0 +1,15 @@ +package com.dfsek.terra.addons.terrascript.parser.lang.variables; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public interface Variable { + T getValue(); + + void setValue(T value); + + Returnable.ReturnType getType(); + + Position getPosition(); +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/StructureScript.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/StructureScript.java new file mode 100644 index 000000000..b260bc8f3 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/StructureScript.java @@ -0,0 +1,185 @@ +package com.dfsek.terra.addons.terrascript.script; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import net.jafama.FastMath; +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.Random; +import java.util.concurrent.ExecutionException; + +import com.dfsek.terra.api.structure.buffer.buffers.DirectBuffer; +import com.dfsek.terra.api.structure.buffer.buffers.StructureBuffer; +import com.dfsek.terra.addons.terrascript.parser.Parser; +import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.BinaryNumberFunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.BiomeFunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.BlockFunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.CheckBlockFunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.CheckFunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.EntityFunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.GetMarkFunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.LootFunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.PullFunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.RandomFunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.RecursionsFunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.SetMarkFunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.StateFunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.StructureFunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.UnaryBooleanFunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.UnaryNumberFunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.UnaryStringFunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.builders.ZeroArgFunctionBuilder; +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.profiler.ProfileFrame; +import com.dfsek.terra.api.registry.Registry; +import com.dfsek.terra.api.structure.LootTable; +import com.dfsek.terra.api.structure.Structure; +import com.dfsek.terra.api.structure.buffer.Buffer; +import com.dfsek.terra.api.structure.rotation.Rotation; +import com.dfsek.terra.api.util.vector.Vector3; +import com.dfsek.terra.api.world.Chunk; +import com.dfsek.terra.api.world.World; + + +public class StructureScript implements Structure { + private final Block block; + private final String id; + private final Cache cache; + private final Platform platform; + + public StructureScript(InputStream inputStream, String id, Platform platform, Registry registry, Registry lootRegistry, + Registry> functionRegistry) { + Parser parser; + try { + parser = new Parser(IOUtils.toString(inputStream, Charset.defaultCharset())); + } catch(IOException e) { + throw new RuntimeException(e); + } + this.id = id; + + functionRegistry.forEach(parser::registerFunction); // Register registry functions. + + parser + .registerFunction("block", new BlockFunctionBuilder(platform)) + .registerFunction("debugBlock", new BlockFunctionBuilder(platform)) + .registerFunction("check", new CheckFunctionBuilder(platform)) + .registerFunction("structure", new StructureFunctionBuilder(registry, platform)) + .registerFunction("randomInt", new RandomFunctionBuilder()) + .registerFunction("recursions", new RecursionsFunctionBuilder()) + .registerFunction("setMark", new SetMarkFunctionBuilder()) + .registerFunction("getMark", new GetMarkFunctionBuilder()) + .registerFunction("pull", new PullFunctionBuilder(platform)) + .registerFunction("loot", new LootFunctionBuilder(platform, lootRegistry, this)) + .registerFunction("entity", new EntityFunctionBuilder(platform)) + .registerFunction("getBiome", new BiomeFunctionBuilder(platform)) + .registerFunction("getBlock", new CheckBlockFunctionBuilder()) + .registerFunction("state", new StateFunctionBuilder(platform)) + .registerFunction("setWaterlog", new UnaryBooleanFunctionBuilder((waterlog, args) -> args.setWaterlog(waterlog))) + .registerFunction("originX", new ZeroArgFunctionBuilder(arguments -> arguments.getBuffer().getOrigin().getX(), + Returnable.ReturnType.NUMBER)) + .registerFunction("originY", new ZeroArgFunctionBuilder(arguments -> arguments.getBuffer().getOrigin().getY(), + Returnable.ReturnType.NUMBER)) + .registerFunction("originZ", new ZeroArgFunctionBuilder(arguments -> arguments.getBuffer().getOrigin().getZ(), + Returnable.ReturnType.NUMBER)) + .registerFunction("rotation", new ZeroArgFunctionBuilder<>(arguments -> arguments.getRotation().toString(), + Returnable.ReturnType.STRING)) + .registerFunction("rotationDegrees", new ZeroArgFunctionBuilder<>(arguments -> arguments.getRotation().getDegrees(), + Returnable.ReturnType.NUMBER)) + .registerFunction("print", + new UnaryStringFunctionBuilder(string -> platform.getDebugLogger().info("[" + id + "] " + string))) + .registerFunction("abs", new UnaryNumberFunctionBuilder(number -> FastMath.abs(number.doubleValue()))) + .registerFunction("pow", new BinaryNumberFunctionBuilder( + (number, number2) -> FastMath.pow(number.doubleValue(), number2.doubleValue()))) + .registerFunction("sqrt", new UnaryNumberFunctionBuilder(number -> FastMath.sqrt(number.doubleValue()))) + .registerFunction("floor", new UnaryNumberFunctionBuilder(number -> FastMath.floor(number.doubleValue()))) + .registerFunction("ceil", new UnaryNumberFunctionBuilder(number -> FastMath.ceil(number.doubleValue()))) + .registerFunction("log", new UnaryNumberFunctionBuilder(number -> FastMath.log(number.doubleValue()))) + .registerFunction("round", new UnaryNumberFunctionBuilder(number -> FastMath.round(number.doubleValue()))) + .registerFunction("sin", new UnaryNumberFunctionBuilder(number -> FastMath.sin(number.doubleValue()))) + .registerFunction("cos", new UnaryNumberFunctionBuilder(number -> FastMath.cos(number.doubleValue()))) + .registerFunction("tan", new UnaryNumberFunctionBuilder(number -> FastMath.tan(number.doubleValue()))) + .registerFunction("asin", new UnaryNumberFunctionBuilder(number -> FastMath.asin(number.doubleValue()))) + .registerFunction("acos", new UnaryNumberFunctionBuilder(number -> FastMath.acos(number.doubleValue()))) + .registerFunction("atan", new UnaryNumberFunctionBuilder(number -> FastMath.atan(number.doubleValue()))) + .registerFunction("max", new BinaryNumberFunctionBuilder( + (number, number2) -> FastMath.max(number.doubleValue(), number2.doubleValue()))) + .registerFunction("min", new BinaryNumberFunctionBuilder( + (number, number2) -> FastMath.min(number.doubleValue(), number2.doubleValue()))); + + if(!platform.getTerraConfig().isDebugScript()) { + parser.ignoreFunction("debugBlock"); + } + + block = parser.parse(); + this.platform = platform; + this.cache = CacheBuilder.newBuilder().maximumSize(platform.getTerraConfig().getStructureCache()).build(); + } + + @Override + @SuppressWarnings("try") + public boolean generate(Vector3 location, World world, Chunk chunk, Random random, Rotation rotation) { + try(ProfileFrame ignore = platform.getProfiler().profile("terrascript_chunk:" + id)) { + StructureBuffer buffer = computeBuffer(location, world, random, rotation); + buffer.paste(location, chunk); + return buffer.succeeded(); + } + } + + @Override + @SuppressWarnings("try") + public boolean generate(Buffer buffer, World world, Random random, Rotation rotation, int recursions) { + try(ProfileFrame ignore = platform.getProfiler().profile("terrascript_recursive:" + id)) { + return applyBlock(new TerraImplementationArguments(buffer, rotation, random, world, recursions)); + } + } + + @Override + @SuppressWarnings("try") + public boolean generate(Vector3 location, World world, Random random, Rotation rotation) { + try(ProfileFrame ignore = platform.getProfiler().profile("terrascript_direct:" + id)) { + DirectBuffer buffer = new DirectBuffer(location, world); + return applyBlock(new TerraImplementationArguments(buffer, rotation, random, world, 0)); + } + } + + @SuppressWarnings("try") + public boolean test(Vector3 location, World world, Random random, Rotation rotation) { + try(ProfileFrame ignore = platform.getProfiler().profile("terrascript_test:" + id)) { + StructureBuffer buffer = computeBuffer(location, world, random, rotation); + return buffer.succeeded(); + } + } + + private StructureBuffer computeBuffer(Vector3 location, World world, Random random, Rotation rotation) { + try { + return cache.get(location, () -> { + StructureBuffer buf = new StructureBuffer(location); + buf.setSucceeded(applyBlock(new TerraImplementationArguments(buf, rotation, random, world, 0))); + return buf; + }); + } catch(ExecutionException e) { + throw new RuntimeException(e); + } + } + + private boolean applyBlock(TerraImplementationArguments arguments) { + try { + return block.apply(arguments).getLevel() != Block.ReturnLevel.FAIL; + } catch(RuntimeException e) { + platform.logger().severe("Failed to generate structure at " + arguments.getBuffer().getOrigin() + ": " + e.getMessage()); + platform.getDebugLogger().stack(e); + return false; + } + } + + @Override + public String getID() { + return id; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/TerraImplementationArguments.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/TerraImplementationArguments.java new file mode 100644 index 000000000..15772fcea --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/TerraImplementationArguments.java @@ -0,0 +1,54 @@ +package com.dfsek.terra.addons.terrascript.script; + +import java.util.Random; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.api.structure.buffer.Buffer; +import com.dfsek.terra.api.structure.rotation.Rotation; +import com.dfsek.terra.api.world.World; + + +public class TerraImplementationArguments implements ImplementationArguments { + private final Buffer buffer; + private final Rotation rotation; + private final Random random; + private final World world; + private final int recursions; + private boolean waterlog = false; + + public TerraImplementationArguments(Buffer buffer, Rotation rotation, Random random, World world, int recursions) { + this.buffer = buffer; + this.rotation = rotation; + this.random = random; + this.world = world; + this.recursions = recursions; + } + + public Buffer getBuffer() { + return buffer; + } + + public int getRecursions() { + return recursions; + } + + public Random getRandom() { + return random; + } + + public Rotation getRotation() { + return rotation; + } + + public boolean isWaterlog() { + return waterlog; + } + + public void setWaterlog(boolean waterlog) { + this.waterlog = waterlog; + } + + public World getWorld() { + return world; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/BinaryNumberFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/BinaryNumberFunctionBuilder.java new file mode 100644 index 000000000..2497db303 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/BinaryNumberFunctionBuilder.java @@ -0,0 +1,55 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class BinaryNumberFunctionBuilder implements FunctionBuilder> { + + private final BiFunction function; + + public BinaryNumberFunctionBuilder(BiFunction function) { + this.function = function; + } + + @Override + public Function build(List> argumentList, Position position) { + return new Function<>() { + @Override + public ReturnType returnType() { + return ReturnType.NUMBER; + } + + @SuppressWarnings("unchecked") + @Override + public Number apply(ImplementationArguments implementationArguments, Map> variableMap) { + return function.apply(((Returnable) argumentList.get(0)).apply(implementationArguments, variableMap), + ((Returnable) argumentList.get(1)).apply(implementationArguments, variableMap)); + } + + @Override + public Position getPosition() { + return position; + } + }; + } + + @Override + public int argNumber() { + return 2; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + if(position == 0 || position == 1) return Returnable.ReturnType.NUMBER; + return null; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/BiomeFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/BiomeFunctionBuilder.java new file mode 100644 index 000000000..7b085b892 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/BiomeFunctionBuilder.java @@ -0,0 +1,38 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.functions.BiomeFunction; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.Platform; + + +public class BiomeFunctionBuilder implements FunctionBuilder { + private final Platform platform; + + public BiomeFunctionBuilder(Platform platform) { + this.platform = platform; + } + + @SuppressWarnings("unchecked") + @Override + public BiomeFunction build(List> argumentList, Position position) { + return new BiomeFunction(platform, (Returnable) argumentList.get(0), (Returnable) argumentList.get(1), + (Returnable) argumentList.get(2), position); + } + + @Override + public int argNumber() { + return 3; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + return switch(position) { + case 0, 1, 2 -> Returnable.ReturnType.NUMBER; + default -> null; + }; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/BlockFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/BlockFunctionBuilder.java new file mode 100644 index 000000000..f99727389 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/BlockFunctionBuilder.java @@ -0,0 +1,52 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; + +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.constants.BooleanConstant; +import com.dfsek.terra.addons.terrascript.parser.lang.constants.StringConstant; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.functions.BlockFunction; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.Platform; + + +public class BlockFunctionBuilder implements FunctionBuilder { + private final Platform platform; + + public BlockFunctionBuilder(Platform platform) { + this.platform = platform; + } + + @SuppressWarnings("unchecked") + @Override + public BlockFunction build(List> argumentList, Position position) { + if(argumentList.size() < 4) throw new ParseException("Expected data", position); + Returnable booleanReturnable = new BooleanConstant(true, position); + if(argumentList.size() == 5) booleanReturnable = (Returnable) argumentList.get(4); + if(argumentList.get(3) instanceof StringConstant) { + return new BlockFunction.Constant((Returnable) argumentList.get(0), (Returnable) argumentList.get(1), + (Returnable) argumentList.get(2), (StringConstant) argumentList.get(3), + booleanReturnable, platform, position); + } + return new BlockFunction((Returnable) argumentList.get(0), (Returnable) argumentList.get(1), + (Returnable) argumentList.get(2), (Returnable) argumentList.get(3), booleanReturnable, + platform, position); + } + + @Override + public int argNumber() { + return -1; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + return switch(position) { + case 0, 1, 2 -> Returnable.ReturnType.NUMBER; + case 3 -> Returnable.ReturnType.STRING; + case 4 -> Returnable.ReturnType.BOOLEAN; + default -> null; + }; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/CheckBlockFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/CheckBlockFunctionBuilder.java new file mode 100644 index 000000000..e86cc4388 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/CheckBlockFunctionBuilder.java @@ -0,0 +1,31 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.functions.CheckBlockFunction; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class CheckBlockFunctionBuilder implements FunctionBuilder { + @SuppressWarnings("unchecked") + @Override + public CheckBlockFunction build(List> argumentList, Position position) { + return new CheckBlockFunction((Returnable) argumentList.get(0), (Returnable) argumentList.get(1), + (Returnable) argumentList.get(2), position); + } + + @Override + public int argNumber() { + return 3; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + return switch(position) { + case 0, 1, 2 -> Returnable.ReturnType.NUMBER; + default -> null; + }; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/CheckFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/CheckFunctionBuilder.java new file mode 100644 index 000000000..483f98947 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/CheckFunctionBuilder.java @@ -0,0 +1,39 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; + +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.functions.CheckFunction; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.Platform; + + +public class CheckFunctionBuilder implements FunctionBuilder { + private final Platform platform; + + public CheckFunctionBuilder(Platform platform) { + this.platform = platform; + } + + @SuppressWarnings("unchecked") + @Override + public CheckFunction build(List> argumentList, Position position) { + return new CheckFunction(platform, (Returnable) argumentList.get(0), (Returnable) argumentList.get(1), + (Returnable) argumentList.get(2), position); + } + + @Override + public int argNumber() { + return 3; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + return switch(position) { + case 0, 1, 2 -> Returnable.ReturnType.NUMBER; + default -> null; + }; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/EntityFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/EntityFunctionBuilder.java new file mode 100644 index 000000000..2c3d3c289 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/EntityFunctionBuilder.java @@ -0,0 +1,40 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; + +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.functions.EntityFunction; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.Platform; + + +public class EntityFunctionBuilder implements FunctionBuilder { + private final Platform platform; + + public EntityFunctionBuilder(Platform platform) { + this.platform = platform; + } + + @SuppressWarnings("unchecked") + @Override + public EntityFunction build(List> argumentList, Position position) { + return new EntityFunction((Returnable) argumentList.get(0), (Returnable) argumentList.get(1), + (Returnable) argumentList.get(2), (Returnable) argumentList.get(3), platform, position); + } + + @Override + public int argNumber() { + return 4; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + return switch(position) { + case 0, 1, 2 -> Returnable.ReturnType.NUMBER; + case 3 -> Returnable.ReturnType.STRING; + default -> null; + }; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/GetMarkFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/GetMarkFunctionBuilder.java new file mode 100644 index 000000000..3973390b7 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/GetMarkFunctionBuilder.java @@ -0,0 +1,35 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.functions.GetMarkFunction; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class GetMarkFunctionBuilder implements FunctionBuilder { + + public GetMarkFunctionBuilder() { + } + + @SuppressWarnings("unchecked") + @Override + public GetMarkFunction build(List> argumentList, Position position) { + return new GetMarkFunction((Returnable) argumentList.get(0), (Returnable) argumentList.get(1), + (Returnable) argumentList.get(2), position); + } + + @Override + public int argNumber() { + return 3; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + return switch(position) { + case 0, 1, 2 -> Returnable.ReturnType.NUMBER; + default -> null; + }; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/LootFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/LootFunctionBuilder.java new file mode 100644 index 000000000..619be08ab --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/LootFunctionBuilder.java @@ -0,0 +1,46 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; + +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.StructureScript; +import com.dfsek.terra.addons.terrascript.script.functions.LootFunction; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.registry.Registry; +import com.dfsek.terra.api.structure.LootTable; + + +public class LootFunctionBuilder implements FunctionBuilder { + private final Platform platform; + private final Registry registry; + private final StructureScript script; + + public LootFunctionBuilder(Platform platform, Registry registry, StructureScript script) { + this.platform = platform; + this.registry = registry; + this.script = script; + } + + @SuppressWarnings("unchecked") + @Override + public LootFunction build(List> argumentList, Position position) { + return new LootFunction(registry, (Returnable) argumentList.get(0), (Returnable) argumentList.get(1), + (Returnable) argumentList.get(2), (Returnable) argumentList.get(3), platform, position, script); + } + + @Override + public int argNumber() { + return 4; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + return switch(position) { + case 0, 1, 2 -> Returnable.ReturnType.NUMBER; + case 3 -> Returnable.ReturnType.STRING; + default -> null; + }; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/PullFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/PullFunctionBuilder.java new file mode 100644 index 000000000..f2cd93cd8 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/PullFunctionBuilder.java @@ -0,0 +1,40 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; + +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.functions.PullFunction; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.Platform; + + +public class PullFunctionBuilder implements FunctionBuilder { + private final Platform platform; + + public PullFunctionBuilder(Platform platform) { + this.platform = platform; + } + + @SuppressWarnings("unchecked") + @Override + public PullFunction build(List> argumentList, Position position) { + return new PullFunction((Returnable) argumentList.get(0), (Returnable) argumentList.get(1), + (Returnable) argumentList.get(2), (Returnable) argumentList.get(3), platform, position); + } + + @Override + public int argNumber() { + return 4; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + return switch(position) { + case 0, 1, 2 -> Returnable.ReturnType.NUMBER; + case 3 -> Returnable.ReturnType.STRING; + default -> null; + }; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/RandomFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/RandomFunctionBuilder.java new file mode 100644 index 000000000..bc0334f65 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/RandomFunctionBuilder.java @@ -0,0 +1,29 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; + +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.functions.RandomFunction; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class RandomFunctionBuilder implements FunctionBuilder { + @SuppressWarnings("unchecked") + @Override + public RandomFunction build(List> argumentList, Position position) { + return new RandomFunction((Returnable) argumentList.get(0), position); + } + + @Override + public int argNumber() { + return 1; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + if(position == 0) return Returnable.ReturnType.NUMBER; + return null; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/RecursionsFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/RecursionsFunctionBuilder.java new file mode 100644 index 000000000..793cd7cb2 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/RecursionsFunctionBuilder.java @@ -0,0 +1,27 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; + +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.functions.RecursionsFunction; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class RecursionsFunctionBuilder implements FunctionBuilder { + @Override + public RecursionsFunction build(List> argumentList, Position position) { + return new RecursionsFunction(position); + } + + @Override + public int argNumber() { + return 0; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + return null; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/SetMarkFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/SetMarkFunctionBuilder.java new file mode 100644 index 000000000..51d269133 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/SetMarkFunctionBuilder.java @@ -0,0 +1,37 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; + +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.functions.SetMarkFunction; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class SetMarkFunctionBuilder implements FunctionBuilder { + + public SetMarkFunctionBuilder() { + } + + @SuppressWarnings("unchecked") + @Override + public SetMarkFunction build(List> argumentList, Position position) { + return new SetMarkFunction((Returnable) argumentList.get(0), (Returnable) argumentList.get(1), + (Returnable) argumentList.get(2), (Returnable) argumentList.get(3), position); + } + + @Override + public int argNumber() { + return 4; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + return switch(position) { + case 0, 1, 2 -> Returnable.ReturnType.NUMBER; + case 3 -> Returnable.ReturnType.STRING; + default -> null; + }; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/StateFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/StateFunctionBuilder.java new file mode 100644 index 000000000..e1b9b41a5 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/StateFunctionBuilder.java @@ -0,0 +1,41 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; + +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.functions.StateFunction; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.Platform; + + +public class StateFunctionBuilder implements FunctionBuilder { + private final Platform platform; + + public StateFunctionBuilder(Platform platform) { + this.platform = platform; + } + + @SuppressWarnings("unchecked") + @Override + public StateFunction build(List> argumentList, Position position) { + if(argumentList.size() < 4) throw new ParseException("Expected data", position); + return new StateFunction((Returnable) argumentList.get(0), (Returnable) argumentList.get(1), + (Returnable) argumentList.get(2), (Returnable) argumentList.get(3), platform, position); + } + + @Override + public int argNumber() { + return 4; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + return switch(position) { + case 0, 1, 2 -> Returnable.ReturnType.NUMBER; + case 3 -> Returnable.ReturnType.STRING; + default -> null; + }; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/StructureFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/StructureFunctionBuilder.java new file mode 100644 index 000000000..95c0a4164 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/StructureFunctionBuilder.java @@ -0,0 +1,48 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; +import java.util.stream.Collectors; + +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.script.functions.StructureFunction; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.registry.Registry; +import com.dfsek.terra.api.structure.Structure; + + +public class StructureFunctionBuilder implements FunctionBuilder { + private final Registry registry; + private final Platform platform; + + public StructureFunctionBuilder(Registry registry, Platform platform) { + this.registry = registry; + this.platform = platform; + } + + @SuppressWarnings("unchecked") + @Override + public StructureFunction build(List> argumentList, Position position) { + if(argumentList.size() < 5) throw new ParseException("Expected rotations", position); + + return new StructureFunction((Returnable) argumentList.remove(0), (Returnable) argumentList.remove(0), + (Returnable) argumentList.remove(0), (Returnable) argumentList.remove(0), + argumentList.stream().map(item -> ((Returnable) item)).collect(Collectors.toList()), registry, + position, platform); + } + + @Override + public int argNumber() { + return -1; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + return switch(position) { + case 0, 1, 2 -> Returnable.ReturnType.NUMBER; + default -> Returnable.ReturnType.STRING; + }; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/UnaryBooleanFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/UnaryBooleanFunctionBuilder.java new file mode 100644 index 000000000..5e81ab78c --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/UnaryBooleanFunctionBuilder.java @@ -0,0 +1,57 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class UnaryBooleanFunctionBuilder implements FunctionBuilder> { + + private final BiConsumer function; + + public UnaryBooleanFunctionBuilder(BiConsumer function) { + this.function = function; + } + + @Override + public Function build(List> argumentList, Position position) { + return new Function<>() { + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } + + @SuppressWarnings("unchecked") + @Override + public Void apply(ImplementationArguments implementationArguments, Map> variableMap) { + function.accept(((Returnable) argumentList.get(0)).apply(implementationArguments, variableMap), + (TerraImplementationArguments) implementationArguments); + return null; + } + + @Override + public Position getPosition() { + return position; + } + }; + } + + @Override + public int argNumber() { + return 1; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + if(position == 0) return Returnable.ReturnType.BOOLEAN; + return null; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/UnaryNumberFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/UnaryNumberFunctionBuilder.java new file mode 100644 index 000000000..06d820a6e --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/UnaryNumberFunctionBuilder.java @@ -0,0 +1,53 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class UnaryNumberFunctionBuilder implements FunctionBuilder> { + + private final java.util.function.Function function; + + public UnaryNumberFunctionBuilder(java.util.function.Function function) { + this.function = function; + } + + @Override + public Function build(List> argumentList, Position position) { + return new Function<>() { + @Override + public ReturnType returnType() { + return ReturnType.NUMBER; + } + + @SuppressWarnings("unchecked") + @Override + public Number apply(ImplementationArguments implementationArguments, Map> variableMap) { + return function.apply(((Returnable) argumentList.get(0)).apply(implementationArguments, variableMap)); + } + + @Override + public Position getPosition() { + return position; + } + }; + } + + @Override + public int argNumber() { + return 1; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + if(position == 0) return Returnable.ReturnType.NUMBER; + return null; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/UnaryStringFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/UnaryStringFunctionBuilder.java new file mode 100644 index 000000000..729f4d345 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/UnaryStringFunctionBuilder.java @@ -0,0 +1,54 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class UnaryStringFunctionBuilder implements FunctionBuilder> { + + private final java.util.function.Consumer function; + + public UnaryStringFunctionBuilder(java.util.function.Consumer function) { + this.function = function; + } + + @Override + public Function build(List> argumentList, Position position) { + return new Function<>() { + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } + + @SuppressWarnings("unchecked") + @Override + public Void apply(ImplementationArguments implementationArguments, Map> variableMap) { + function.accept(((Returnable) argumentList.get(0)).apply(implementationArguments, variableMap)); + return null; + } + + @Override + public Position getPosition() { + return position; + } + }; + } + + @Override + public int argNumber() { + return 1; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + if(position == 0) return Returnable.ReturnType.STRING; + return null; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/ZeroArgFunctionBuilder.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/ZeroArgFunctionBuilder.java new file mode 100644 index 000000000..4cb828d38 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/builders/ZeroArgFunctionBuilder.java @@ -0,0 +1,54 @@ +package com.dfsek.terra.addons.terrascript.script.builders; + +import java.util.List; +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class ZeroArgFunctionBuilder implements FunctionBuilder> { + private final java.util.function.Function function; + private final Returnable.ReturnType type; + + public ZeroArgFunctionBuilder(java.util.function.Function function, Returnable.ReturnType type) { + this.function = function; + this.type = type; + } + + @Override + public Function build(List> argumentList, Position position) { + return new Function<>() { + @Override + public ReturnType returnType() { + return type; + } + + @Override + public T apply(ImplementationArguments implementationArguments, Map> variableMap) { + return function.apply((TerraImplementationArguments) implementationArguments); + } + + @Override + public Position getPosition() { + return position; + } + }; + } + + @Override + public int argNumber() { + return 0; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + if(position == 0) return type; + return null; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/BiomeFunction.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/BiomeFunction.java new file mode 100644 index 000000000..3619a02db --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/BiomeFunction.java @@ -0,0 +1,64 @@ +package com.dfsek.terra.addons.terrascript.script.functions; + +import com.dfsek.terra.api.Platform; + +import net.jafama.FastMath; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.util.RotationUtil; +import com.dfsek.terra.api.util.vector.Vector2; +import com.dfsek.terra.api.util.vector.Vector3; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; + + +public class BiomeFunction implements Function { + private final Platform platform; + private final Returnable x, y, z; + private final Position position; + + + public BiomeFunction(Platform platform, Returnable x, Returnable y, Returnable z, Position position) { + this.platform = platform; + this.x = x; + this.y = y; + this.z = z; + this.position = position; + } + + + @Override + public String apply(ImplementationArguments implementationArguments, Map> variableMap) { + TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments; + + Vector2 xz = new Vector2(x.apply(implementationArguments, variableMap).doubleValue(), + z.apply(implementationArguments, variableMap).doubleValue()); + + RotationUtil.rotateVector(xz, arguments.getRotation()); + + BiomeProvider grid = arguments.getWorld().getBiomeProvider(); + + return grid.getBiome(arguments.getBuffer() + .getOrigin() + .clone() + .add(new Vector3(FastMath.roundToInt(xz.getX()), + y.apply(implementationArguments, variableMap).intValue(), + FastMath.roundToInt(xz.getZ()))), arguments.getWorld().getSeed()).getID(); + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.STRING; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/BlockFunction.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/BlockFunction.java new file mode 100644 index 000000000..d34803f8a --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/BlockFunction.java @@ -0,0 +1,94 @@ +package com.dfsek.terra.addons.terrascript.script.functions; + +import com.dfsek.terra.api.Platform; + +import net.jafama.FastMath; + +import java.util.HashMap; +import java.util.Map; + +import com.dfsek.terra.api.structure.buffer.items.BufferedBlock; +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.constants.StringConstant; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.block.state.BlockState; +import com.dfsek.terra.api.util.RotationUtil; +import com.dfsek.terra.api.util.vector.Vector2; +import com.dfsek.terra.api.util.vector.Vector3; + + +public class BlockFunction implements Function { + protected final Returnable x, y, z; + protected final Returnable blockData; + protected final Platform platform; + private final Map data = new HashMap<>(); + private final Returnable overwrite; + private final Position position; + + public BlockFunction(Returnable x, Returnable y, Returnable z, Returnable blockData, + Returnable overwrite, Platform platform, Position position) { + this.x = x; + this.y = y; + this.z = z; + this.blockData = blockData; + this.overwrite = overwrite; + this.platform = platform; + this.position = position; + } + + @Override + public Void apply(ImplementationArguments implementationArguments, Map> variableMap) { + TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments; + BlockState rot = getBlockState(implementationArguments, variableMap).clone(); + setBlock(implementationArguments, variableMap, arguments, rot); + return null; + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } + + void setBlock(ImplementationArguments implementationArguments, Map> variableMap, + TerraImplementationArguments arguments, BlockState rot) { + Vector2 xz = new Vector2(x.apply(implementationArguments, variableMap).doubleValue(), + z.apply(implementationArguments, variableMap).doubleValue()); + + RotationUtil.rotateVector(xz, arguments.getRotation()); + + RotationUtil.rotateBlockData(rot, arguments.getRotation().inverse()); + arguments.getBuffer().addItem( + new BufferedBlock(rot, overwrite.apply(implementationArguments, variableMap), platform, arguments.isWaterlog()), + new Vector3(FastMath.roundToInt(xz.getX()), y.apply(implementationArguments, variableMap).doubleValue(), + FastMath.roundToInt(xz.getZ()))); + } + + protected BlockState getBlockState(ImplementationArguments arguments, Map> variableMap) { + return data.computeIfAbsent(blockData.apply(arguments, variableMap), platform.getWorldHandle()::createBlockData); + } + + + public static class Constant extends BlockFunction { + private final BlockState state; + + public Constant(Returnable x, Returnable y, Returnable z, StringConstant blockData, + Returnable overwrite, Platform platform, Position position) { + super(x, y, z, blockData, overwrite, platform, position); + this.state = platform.getWorldHandle().createBlockData(blockData.getConstant()); + } + + @Override + protected BlockState getBlockState(ImplementationArguments arguments, Map> variableMap) { + return state; + } + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/CheckBlockFunction.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/CheckBlockFunction.java new file mode 100644 index 000000000..2b60afbd5 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/CheckBlockFunction.java @@ -0,0 +1,60 @@ +package com.dfsek.terra.addons.terrascript.script.functions; + +import net.jafama.FastMath; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.util.RotationUtil; +import com.dfsek.terra.api.util.vector.Vector2; +import com.dfsek.terra.api.util.vector.Vector3; + + +public class CheckBlockFunction implements Function { + private final Returnable x, y, z; + private final Position position; + + public CheckBlockFunction(Returnable x, Returnable y, Returnable z, Position position) { + this.x = x; + this.y = y; + this.z = z; + this.position = position; + } + + + @Override + public String apply(ImplementationArguments implementationArguments, Map> variableMap) { + TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments; + + Vector2 xz = new Vector2(x.apply(implementationArguments, variableMap).doubleValue(), + z.apply(implementationArguments, variableMap).doubleValue()); + + RotationUtil.rotateVector(xz, arguments.getRotation()); + + String data = arguments.getWorld() + .getBlockData(arguments.getBuffer() + .getOrigin() + .clone() + .add(new Vector3(FastMath.roundToInt(xz.getX()), + y.apply(implementationArguments, variableMap) + .doubleValue(), FastMath.roundToInt(xz.getZ())))) + .getAsString(); + if(data.contains("[")) return data.substring(0, data.indexOf('[')); // Strip properties + else return data; + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.STRING; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/CheckFunction.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/CheckFunction.java new file mode 100644 index 000000000..b975fa990 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/CheckFunction.java @@ -0,0 +1,85 @@ +package com.dfsek.terra.addons.terrascript.script.functions; + +import net.jafama.FastMath; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.util.RotationUtil; +import com.dfsek.terra.api.util.vector.Vector2; +import com.dfsek.terra.api.util.vector.Vector3; +import com.dfsek.terra.api.world.World; +import com.dfsek.terra.api.world.generator.SamplerCache; + + +public class CheckFunction implements Function { + private final Platform platform; + private final Returnable x, y, z; + private final Position position; + + public CheckFunction(Platform platform, Returnable x, Returnable y, Returnable z, Position position) { + this.platform = platform; + this.x = x; + this.y = y; + this.z = z; + this.position = position; + } + + + @Override + public String apply(ImplementationArguments implementationArguments, Map> variableMap) { + + + TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments; + + + Vector2 xz = new Vector2(x.apply(implementationArguments, variableMap).doubleValue(), + z.apply(implementationArguments, variableMap).doubleValue()); + + RotationUtil.rotateVector(xz, arguments.getRotation()); + + Vector3 location = arguments.getBuffer().getOrigin().clone().add( + new Vector3(FastMath.roundToInt(xz.getX()), y.apply(implementationArguments, variableMap).doubleValue(), + FastMath.roundToInt(xz.getZ()))); + + return apply(location, arguments.getWorld()); + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.STRING; + } + + private String apply(Vector3 vector, World world) { + int y = vector.getBlockY(); + if(y >= world.getMaxHeight() || y < 0) return "AIR"; + SamplerCache cache = world.getConfig().getSamplerCache(); + double comp = sample(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ(), cache); + + if(comp > 0) return "LAND"; // If noise val is greater than zero, location will always be land. + + //BiomeProvider provider = tw.getBiomeProvider(); + //TerraBiome b = provider.getBiome(vector.getBlockX(), vector.getBlockZ()); + + /*if(vector.getY() > c.getSeaLevel())*/ + return "AIR"; // Above sea level + //return "OCEAN"; // Below sea level + } + + private double sample(int x, int y, int z, SamplerCache cache) { + int cx = FastMath.floorDiv(x, 16); + int cz = FastMath.floorDiv(z, 16); + return cache.get(x, z).sample(x - (cx << 4), y, z - (cz << 4)); + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/EntityFunction.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/EntityFunction.java new file mode 100644 index 000000000..cae74890e --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/EntityFunction.java @@ -0,0 +1,61 @@ +package com.dfsek.terra.addons.terrascript.script.functions; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.buffer.items.BufferedEntity; +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.constants.ConstantExpression; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.entity.EntityType; +import com.dfsek.terra.api.util.RotationUtil; +import com.dfsek.terra.api.util.vector.Vector2; +import com.dfsek.terra.api.util.vector.Vector3; + + +public class EntityFunction implements Function { + private final EntityType data; + private final Returnable x, y, z; + private final Position position; + private final Platform platform; + + public EntityFunction(Returnable x, Returnable y, Returnable z, Returnable data, Platform platform, + Position position) { + this.position = position; + this.platform = platform; + if(!(data instanceof ConstantExpression)) throw new ParseException("Entity data must be constant", data.getPosition()); + + this.data = platform.getWorldHandle().getEntity(((ConstantExpression) data).getConstant()); + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public Void apply(ImplementationArguments implementationArguments, Map> variableMap) { + TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments; + Vector2 xz = new Vector2(x.apply(implementationArguments, variableMap).doubleValue(), + z.apply(implementationArguments, variableMap).doubleValue()); + + RotationUtil.rotateVector(xz, arguments.getRotation()); + + arguments.getBuffer().addItem(new BufferedEntity(data, platform), + new Vector3(xz.getX(), y.apply(implementationArguments, variableMap).doubleValue(), xz.getZ())); + return null; + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/GetMarkFunction.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/GetMarkFunction.java new file mode 100644 index 000000000..2f1fa4a6c --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/GetMarkFunction.java @@ -0,0 +1,50 @@ +package com.dfsek.terra.addons.terrascript.script.functions; + +import net.jafama.FastMath; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.util.RotationUtil; +import com.dfsek.terra.api.util.vector.Vector2; +import com.dfsek.terra.api.util.vector.Vector3; + + +public class GetMarkFunction implements Function { + private final Returnable x, y, z; + private final Position position; + + public GetMarkFunction(Returnable x, Returnable y, Returnable z, Position position) { + this.position = position; + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public String apply(ImplementationArguments implementationArguments, Map> variableMap) { + TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments; + Vector2 xz = new Vector2(x.apply(implementationArguments, variableMap).doubleValue(), + z.apply(implementationArguments, variableMap).doubleValue()); + + RotationUtil.rotateVector(xz, arguments.getRotation()); + String mark = arguments.getBuffer().getMark(new Vector3(FastMath.floorToInt(xz.getX()), FastMath.floorToInt( + y.apply(implementationArguments, variableMap).doubleValue()), FastMath.floorToInt(xz.getZ()))); + return mark == null ? "" : mark; + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.STRING; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/LootFunction.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/LootFunction.java new file mode 100644 index 000000000..f0839ff10 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/LootFunction.java @@ -0,0 +1,74 @@ +package com.dfsek.terra.addons.terrascript.script.functions; + +import net.jafama.FastMath; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.buffer.items.BufferedLootApplication; +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.script.StructureScript; +import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.registry.Registry; +import com.dfsek.terra.api.structure.LootTable; +import com.dfsek.terra.api.util.RotationUtil; +import com.dfsek.terra.api.util.vector.Vector2; +import com.dfsek.terra.api.util.vector.Vector3; + + +public class LootFunction implements Function { + private final Registry registry; + private final Returnable data; + private final Returnable x, y, z; + private final Position position; + private final Platform platform; + private final StructureScript script; + + public LootFunction(Registry registry, Returnable x, Returnable y, Returnable z, + Returnable data, Platform platform, Position position, StructureScript script) { + this.registry = registry; + this.position = position; + this.data = data; + this.x = x; + this.y = y; + this.z = z; + this.platform = platform; + this.script = script; + } + + @Override + public Void apply(ImplementationArguments implementationArguments, Map> variableMap) { + TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments; + Vector2 xz = new Vector2(x.apply(implementationArguments, variableMap).doubleValue(), + z.apply(implementationArguments, variableMap).doubleValue()); + + RotationUtil.rotateVector(xz, arguments.getRotation()); + + String id = data.apply(implementationArguments, variableMap); + LootTable table = registry.get(id); + + if(table == null) { + platform.logger().severe("No such loot table " + id); + return null; + } + + arguments.getBuffer().addItem(new BufferedLootApplication(table, platform, script), + new Vector3(FastMath.roundToInt(xz.getX()), y.apply(implementationArguments, variableMap).intValue(), + FastMath.roundToInt(xz.getZ()))); + return null; + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/PullFunction.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/PullFunction.java new file mode 100644 index 000000000..b80310839 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/PullFunction.java @@ -0,0 +1,63 @@ +package com.dfsek.terra.addons.terrascript.script.functions; + +import net.jafama.FastMath; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.buffer.items.BufferedPulledBlock; +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.constants.ConstantExpression; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.block.state.BlockState; +import com.dfsek.terra.api.util.RotationUtil; +import com.dfsek.terra.api.util.vector.Vector2; +import com.dfsek.terra.api.util.vector.Vector3; + + +public class PullFunction implements Function { + private final BlockState data; + private final Returnable x, y, z; + private final Position position; + + public PullFunction(Returnable x, Returnable y, Returnable z, Returnable data, Platform platform, + Position position) { + this.position = position; + if(!(data instanceof ConstantExpression)) throw new ParseException("Block data must be constant", data.getPosition()); + + this.data = platform.getWorldHandle().createBlockData(((ConstantExpression) data).getConstant()); + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public Void apply(ImplementationArguments implementationArguments, Map> variableMap) { + TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments; + Vector2 xz = new Vector2(x.apply(implementationArguments, variableMap).doubleValue(), + z.apply(implementationArguments, variableMap).doubleValue()); + + RotationUtil.rotateVector(xz, arguments.getRotation()); + BlockState rot = data.clone(); + RotationUtil.rotateBlockData(rot, arguments.getRotation().inverse()); + arguments.getBuffer().addItem(new BufferedPulledBlock(rot), + new Vector3(FastMath.roundToInt(xz.getX()), y.apply(implementationArguments, variableMap).intValue(), + FastMath.roundToInt(xz.getZ()))); + return null; + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/RandomFunction.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/RandomFunction.java new file mode 100644 index 000000000..6cec0dcca --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/RandomFunction.java @@ -0,0 +1,38 @@ +package com.dfsek.terra.addons.terrascript.script.functions; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class RandomFunction implements Function { + private final Returnable numberReturnable; + private final Position position; + + public RandomFunction(Returnable numberReturnable, Position position) { + this.numberReturnable = numberReturnable; + this.position = position; + } + + + @Override + public ReturnType returnType() { + return ReturnType.NUMBER; + } + + @Override + public Integer apply(ImplementationArguments implementationArguments, Map> variableMap) { + return ((TerraImplementationArguments) implementationArguments).getRandom().nextInt( + numberReturnable.apply(implementationArguments, variableMap).intValue()); + } + + @Override + public Position getPosition() { + return position; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/RecursionsFunction.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/RecursionsFunction.java new file mode 100644 index 000000000..0d4ce39d4 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/RecursionsFunction.java @@ -0,0 +1,33 @@ +package com.dfsek.terra.addons.terrascript.script.functions; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class RecursionsFunction implements Function { + private final Position position; + + public RecursionsFunction(Position position) { + this.position = position; + } + + @Override + public ReturnType returnType() { + return ReturnType.NUMBER; + } + + @Override + public Number apply(ImplementationArguments implementationArguments, Map> variableMap) { + return ((TerraImplementationArguments) implementationArguments).getRecursions(); + } + + @Override + public Position getPosition() { + return position; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/SetMarkFunction.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/SetMarkFunction.java new file mode 100644 index 000000000..803da7bfd --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/SetMarkFunction.java @@ -0,0 +1,57 @@ +package com.dfsek.terra.addons.terrascript.script.functions; + +import net.jafama.FastMath; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.util.RotationUtil; +import com.dfsek.terra.api.util.vector.Vector2; +import com.dfsek.terra.api.util.vector.Vector3; + + +public class SetMarkFunction implements Function { + private final Returnable x, y, z; + private final Position position; + private final Returnable mark; + + public SetMarkFunction(Returnable x, Returnable y, Returnable z, Returnable mark, Position position) { + this.position = position; + this.mark = mark; + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public Void apply(ImplementationArguments implementationArguments, Map> variableMap) { + TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments; + Vector2 xz = new Vector2(x.apply(implementationArguments, variableMap).doubleValue(), + z.apply(implementationArguments, variableMap).doubleValue()); + + RotationUtil.rotateVector(xz, arguments.getRotation()); + + arguments.getBuffer().setMark(mark.apply(implementationArguments, variableMap), new Vector3(FastMath.floorToInt(xz.getX()), + FastMath.floorToInt( + y.apply(implementationArguments, + variableMap) + .doubleValue()), + FastMath.floorToInt(xz.getZ()))); + return null; + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/StateFunction.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/StateFunction.java new file mode 100644 index 000000000..26bc53ed5 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/StateFunction.java @@ -0,0 +1,58 @@ +package com.dfsek.terra.addons.terrascript.script.functions; + +import net.jafama.FastMath; + +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.buffer.items.BufferedStateManipulator; +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.util.RotationUtil; +import com.dfsek.terra.api.util.vector.Vector2; +import com.dfsek.terra.api.util.vector.Vector3; + + +public class StateFunction implements Function { + private final Returnable data; + private final Returnable x, y, z; + private final Position position; + private final Platform platform; + + public StateFunction(Returnable x, Returnable y, Returnable z, Returnable data, Platform platform, + Position position) { + this.position = position; + this.platform = platform; + this.data = data; + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public Void apply(ImplementationArguments implementationArguments, Map> variableMap) { + TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments; + Vector2 xz = new Vector2(x.apply(implementationArguments, variableMap).doubleValue(), + z.apply(implementationArguments, variableMap).doubleValue()); + RotationUtil.rotateVector(xz, arguments.getRotation()); + + arguments.getBuffer().addItem(new BufferedStateManipulator(platform, data.apply(implementationArguments, variableMap)), + new Vector3(FastMath.roundToInt(xz.getX()), y.apply(implementationArguments, variableMap).intValue(), + FastMath.roundToInt(xz.getZ()))); + return null; + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/StructureFunction.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/StructureFunction.java new file mode 100644 index 000000000..edcf262c1 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/script/functions/StructureFunction.java @@ -0,0 +1,88 @@ +package com.dfsek.terra.addons.terrascript.script.functions; + +import net.jafama.FastMath; + +import java.util.List; +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.buffer.IntermediateBuffer; +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.registry.Registry; +import com.dfsek.terra.api.structure.Structure; +import com.dfsek.terra.api.structure.rotation.Rotation; +import com.dfsek.terra.api.util.RotationUtil; +import com.dfsek.terra.api.util.vector.Vector2; +import com.dfsek.terra.api.util.vector.Vector3; + + +public class StructureFunction implements Function { + private final Registry registry; + private final Returnable id; + private final Returnable x, y, z; + private final Position position; + private final Platform platform; + private final List> rotations; + + public StructureFunction(Returnable x, Returnable y, Returnable z, Returnable id, + List> rotations, Registry registry, Position position, Platform platform) { + this.registry = registry; + this.id = id; + this.position = position; + this.x = x; + this.y = y; + this.z = z; + this.platform = platform; + this.rotations = rotations; + } + + @Override + public ReturnType returnType() { + return ReturnType.BOOLEAN; + } + + @Override + public Boolean apply(ImplementationArguments implementationArguments, Map> variableMap) { + TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments; + + if(arguments.getRecursions() > platform.getTerraConfig().getMaxRecursion()) + throw new RuntimeException("Structure recursion too deep: " + arguments.getRecursions()); + + Vector2 xz = new Vector2(x.apply(implementationArguments, variableMap).doubleValue(), + z.apply(implementationArguments, variableMap).doubleValue()); + + RotationUtil.rotateVector(xz, arguments.getRotation()); + + String app = id.apply(implementationArguments, variableMap); + Structure script = registry.get(app); + if(script == null) { + platform.logger().severe("No such structure " + app); + return null; + } + + Rotation rotation1; + String rotString = rotations.get(arguments.getRandom().nextInt(rotations.size())).apply(implementationArguments, variableMap); + try { + rotation1 = Rotation.valueOf(rotString); + } catch(IllegalArgumentException e) { + platform.logger().severe("Invalid rotation " + rotString); + return null; + } + + Vector3 offset = new Vector3(FastMath.roundToInt(xz.getX()), y.apply(implementationArguments, variableMap).doubleValue(), + FastMath.roundToInt(xz.getZ())); + + return script.generate(new IntermediateBuffer(arguments.getBuffer(), offset), arguments.getWorld(), arguments.getRandom(), + arguments.getRotation().rotate(rotation1), arguments.getRecursions() + 1); + } + + @Override + public Position getPosition() { + return position; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Char.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Char.java new file mode 100644 index 000000000..7eb21418f --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Char.java @@ -0,0 +1,56 @@ +package com.dfsek.terra.addons.terrascript.tokenizer; + +public class Char { + private final char character; + private final int index; + private final int line; + + + public Char(char character, int index, int line) { + this.character = character; + this.index = index; + this.line = line; + } + + public boolean is(char... tests) { + for(char test : tests) { + if(test == character && test != '\0') { + return true; + } + } + return false; + } + + @Override + public String toString() { + return Character.toString(character); + } + + public char getCharacter() { + return character; + } + + public int getIndex() { + return index; + } + + public int getLine() { + return line; + } + + public boolean isWhitespace() { + return Character.isWhitespace(character); + } + + public boolean isNewLine() { + return character == '\n'; + } + + public boolean isDigit() { + return Character.isDigit(character); + } + + public boolean isEOF() { + return character == '\0'; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Lookahead.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Lookahead.java new file mode 100644 index 000000000..e65c6476f --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Lookahead.java @@ -0,0 +1,123 @@ +package com.dfsek.terra.addons.terrascript.tokenizer; + + +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.List; + + +/** + * Stream-like data structure that allows viewing future elements without consuming current. + */ +public class Lookahead { + private final List buffer = new ArrayList<>(); + private final Reader input; + private int index = 0; + private int line = 0; + private boolean end = false; + + public Lookahead(Reader r) { + this.input = r; + } + + /** + * Get the current character without consuming it. + * + * @return current character + */ + public Char current() { + return next(0); + } + + + /** + * Consume and return one character. + * + * @return Character that was consumed. + */ + public Char consume() { + Char consumed = current(); + consume(1); + return consumed; + } + + /** + * Fetch a future character without consuming it. + * + * @param ahead Distance ahead to peek + * + * @return Character + */ + public Char next(int ahead) { + if(ahead < 0) throw new IllegalArgumentException(); + + while(buffer.size() <= ahead && !end) { + Char item = fetch(); + if(item != null) { + buffer.add(item); + } else end = true; + } + + if(ahead >= buffer.size()) { + return null; + } else return buffer.get(ahead); + } + + /** + * Consume an amount of characters + * + * @param amount Number of characters to consume + */ + public void consume(int amount) { + if(amount < 0) throw new IllegalArgumentException(); + while(amount-- > 0) { + if(!buffer.isEmpty()) buffer.remove(0); // Remove top item from buffer. + else { + if(end) return; + Char item = fetch(); + if(item == null) end = true; + } + } + } + + public boolean matches(String check, boolean consume) { + if(check == null) return false; + + for(int i = 0; i < check.length(); i++) { + if(!next(i).is(check.charAt(i))) return false; + } + + if(consume) consume(check.length()); // Consume string + return true; + } + + /** + * Fetch the next character. + * + * @return Next character + */ + private Char fetch() { + try { + int c = input.read(); + if(c == -1) return null; + if(c == '\n') { + line++; + index = 0; + } + index++; + return new Char((char) c, line, index); + } catch(IOException e) { + e.printStackTrace(); + return null; + } + } + + public int getLine() { + return line; + } + + public int getIndex() { + return index; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Position.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Position.java new file mode 100644 index 000000000..f57663c44 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Position.java @@ -0,0 +1,16 @@ +package com.dfsek.terra.addons.terrascript.tokenizer; + +public class Position { + private final int line; + private final int index; + + public Position(int line, int index) { + this.line = line; + this.index = index; + } + + @Override + public String toString() { + return (line + 1) + ":" + index; + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Token.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Token.java new file mode 100644 index 000000000..578dc65b4 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Token.java @@ -0,0 +1,230 @@ +package com.dfsek.terra.addons.terrascript.tokenizer; + +public class Token { + private final String content; + private final Type type; + private final Position start; + + public Token(String content, Type type, Position start) { + this.content = content; + this.type = type; + this.start = start; + } + + @Override + public String toString() { + return type + ": '" + content + "'"; + } + + public Type getType() { + return type; + } + + public String getContent() { + return content; + } + + public Position getPosition() { + return start; + } + + public boolean isConstant() { + return this.type.equals(Type.NUMBER) || this.type.equals(Type.STRING) || this.type.equals(Type.BOOLEAN); + } + + public boolean isBinaryOperator() { + return type.equals(Type.ADDITION_OPERATOR) + || type.equals(Type.SUBTRACTION_OPERATOR) + || type.equals(Type.MULTIPLICATION_OPERATOR) + || type.equals(Type.DIVISION_OPERATOR) + || type.equals(Type.EQUALS_OPERATOR) + || type.equals(Type.NOT_EQUALS_OPERATOR) + || type.equals(Type.LESS_THAN_OPERATOR) + || type.equals(Type.GREATER_THAN_OPERATOR) + || type.equals(Type.LESS_THAN_OR_EQUALS_OPERATOR) + || type.equals(Type.GREATER_THAN_OR_EQUALS_OPERATOR) + || type.equals(Type.BOOLEAN_OR) + || type.equals(Type.BOOLEAN_AND) + || type.equals(Type.MODULO_OPERATOR); + } + + public boolean isStrictNumericOperator() { + return type.equals(Type.SUBTRACTION_OPERATOR) + || type.equals(Type.MULTIPLICATION_OPERATOR) + || type.equals(Type.DIVISION_OPERATOR) + || type.equals(Type.GREATER_THAN_OPERATOR) + || type.equals(Type.LESS_THAN_OPERATOR) + || type.equals(Type.LESS_THAN_OR_EQUALS_OPERATOR) + || type.equals(Type.GREATER_THAN_OR_EQUALS_OPERATOR) + || type.equals(Type.MODULO_OPERATOR); + } + + public boolean isStrictBooleanOperator() { + return type.equals(Type.BOOLEAN_AND) + || type.equals(Type.BOOLEAN_OR); + } + + public boolean isVariableDeclaration() { + return type.equals(Type.STRING_VARIABLE) + || type.equals(Type.BOOLEAN_VARIABLE) + || type.equals(Type.NUMBER_VARIABLE); + } + + public boolean isLoopLike() { + return type.equals(Type.IF_STATEMENT) + || type.equals(Type.WHILE_LOOP) + || type.equals(Type.FOR_LOOP); + } + + public boolean isIdentifier() { + return type.equals(Type.IDENTIFIER); + } + + public enum Type { + /** + * Function identifier or language keyword + */ + IDENTIFIER, + + /** + * Numeric literal + */ + NUMBER, + /** + * String literal + */ + STRING, + /** + * Boolean literal + */ + BOOLEAN, + /** + * Beginning of group + */ + GROUP_BEGIN, + /** + * Ending of group + */ + GROUP_END, + /** + * End of statement + */ + STATEMENT_END, + /** + * Argument separator + */ + SEPARATOR, + /** + * Beginning of code block + */ + BLOCK_BEGIN, + /** + * End of code block + */ + BLOCK_END, + /** + * assignment operator + */ + ASSIGNMENT, + /** + * Boolean equals operator + */ + EQUALS_OPERATOR, + /** + * Boolean not equals operator + */ + NOT_EQUALS_OPERATOR, + /** + * Boolean greater than operator + */ + GREATER_THAN_OPERATOR, + /** + * Boolean less than operator + */ + LESS_THAN_OPERATOR, + /** + * Boolean greater than or equal to operator + */ + GREATER_THAN_OR_EQUALS_OPERATOR, + /** + * Boolean less than or equal to operator + */ + LESS_THAN_OR_EQUALS_OPERATOR, + /** + * Addition/concatenation operator + */ + ADDITION_OPERATOR, + /** + * Subtraction operator + */ + SUBTRACTION_OPERATOR, + /** + * Multiplication operator + */ + MULTIPLICATION_OPERATOR, + /** + * Division operator + */ + DIVISION_OPERATOR, + /** + * Modulo operator. + */ + MODULO_OPERATOR, + /** + * Boolean not operator + */ + BOOLEAN_NOT, + /** + * Boolean or + */ + BOOLEAN_OR, + /** + * Boolean and + */ + BOOLEAN_AND, + /** + * Numeric variable declaration + */ + NUMBER_VARIABLE, + /** + * String variable declaration + */ + STRING_VARIABLE, + /** + * Boolean variable declaration + */ + BOOLEAN_VARIABLE, + /** + * If statement declaration + */ + IF_STATEMENT, + /** + * While loop declaration + */ + WHILE_LOOP, + /** + * Return statement + */ + RETURN, + /** + * Continue statement + */ + CONTINUE, + /** + * Break statement + */ + BREAK, + /** + * Fail statement. Like return keyword, but specifies that generation has failed. + */ + FAIL, + /** + * For loop initializer token + */ + FOR_LOOP, + /** + * Else keyword + */ + ELSE + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Tokenizer.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Tokenizer.java new file mode 100644 index 000000000..5c82f1b81 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/Tokenizer.java @@ -0,0 +1,240 @@ +package com.dfsek.terra.addons.terrascript.tokenizer; + +import com.google.common.collect.Sets; + +import java.io.StringReader; +import java.util.Set; +import java.util.Stack; + +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.tokenizer.exceptions.EOFException; +import com.dfsek.terra.addons.terrascript.tokenizer.exceptions.FormatException; +import com.dfsek.terra.addons.terrascript.tokenizer.exceptions.TokenizerException; + + +public class Tokenizer { + public static final Set syntaxSignificant = Sets.newHashSet(';', '(', ')', '"', ',', '\\', '=', '{', '}', '+', '-', '*', '/', + '>', '<', '!'); // Reserved chars + private final Lookahead reader; + private final Stack brackets = new Stack<>(); + private Token current; + private Token last; + + public Tokenizer(String data) { + reader = new Lookahead(new StringReader(data + '\0')); + current = fetchCheck(); + } + + /** + * Get the first token. + * + * @return First token + * + * @throws ParseException If token does not exist + */ + public Token get() { + if(!hasNext()) throw new ParseException("Unexpected end of input", last.getPosition()); + return current; + } + + /** + * Consume (get and remove) the first token. + * + * @return First token + * + * @throws ParseException If token does not exist + */ + public Token consume() { + if(!hasNext()) throw new ParseException("Unexpected end of input", last.getPosition()); + Token temp = current; + current = fetchCheck(); + return temp; + } + + private Token fetchCheck() { + Token fetch = fetch(); + if(fetch != null) { + last = fetch; + if(fetch.getType() == Token.Type.BLOCK_BEGIN) brackets.push(fetch); // Opening bracket + else if(fetch.getType() == Token.Type.BLOCK_END) { + if(!brackets.isEmpty()) brackets.pop(); + else throw new ParseException("Dangling opening brace", new Position(0, 0)); + } + } else if(!brackets.isEmpty()) { + throw new ParseException("Dangling closing brace", brackets.peek().getPosition()); + } + return fetch; + } + + private Token fetch() throws TokenizerException { + while(!reader.current().isEOF() && reader.current().isWhitespace()) reader.consume(); + + while(reader.matches("//", true)) skipLine(); // Skip line if comment + + if(reader.matches("/*", true)) skipTo("*/"); // Skip multi line comment + + if(reader.current().isEOF()) return null; // EOF + + if(reader.matches("==", true)) + return new Token("==", Token.Type.EQUALS_OPERATOR, new Position(reader.getLine(), reader.getIndex())); + if(reader.matches("!=", true)) + return new Token("!=", Token.Type.NOT_EQUALS_OPERATOR, new Position(reader.getLine(), reader.getIndex())); + if(reader.matches(">=", true)) + return new Token(">=", Token.Type.GREATER_THAN_OR_EQUALS_OPERATOR, new Position(reader.getLine(), reader.getIndex())); + if(reader.matches("<=", true)) + return new Token("<=", Token.Type.LESS_THAN_OR_EQUALS_OPERATOR, new Position(reader.getLine(), reader.getIndex())); + if(reader.matches(">", true)) + return new Token(">", Token.Type.GREATER_THAN_OPERATOR, new Position(reader.getLine(), reader.getIndex())); + if(reader.matches("<", true)) + return new Token("<", Token.Type.LESS_THAN_OPERATOR, new Position(reader.getLine(), reader.getIndex())); + + + if(reader.matches("||", true)) + return new Token("||", Token.Type.BOOLEAN_OR, new Position(reader.getLine(), reader.getIndex())); + if(reader.matches("&&", true)) + return new Token("&&", Token.Type.BOOLEAN_AND, new Position(reader.getLine(), reader.getIndex())); + + + if(isNumberStart()) { + StringBuilder num = new StringBuilder(); + while(!reader.current().isEOF() && isNumberLike()) { + num.append(reader.consume()); + } + return new Token(num.toString(), Token.Type.NUMBER, new Position(reader.getLine(), reader.getIndex())); + } + + if(reader.current().is('"')) { + reader.consume(); // Consume first quote + StringBuilder string = new StringBuilder(); + boolean ignoreNext = false; + while((!reader.current().is('"')) || ignoreNext) { + if(reader.current().is('\\') && !ignoreNext) { + ignoreNext = true; + reader.consume(); + continue; + } else ignoreNext = false; + if(reader.current().isEOF()) + throw new FormatException("No end of string literal found. ", new Position(reader.getLine(), reader.getIndex())); + string.append(reader.consume()); + } + reader.consume(); // Consume last quote + + return new Token(string.toString(), Token.Type.STRING, new Position(reader.getLine(), reader.getIndex())); + } + + if(reader.current().is('(')) + return new Token(reader.consume().toString(), Token.Type.GROUP_BEGIN, new Position(reader.getLine(), reader.getIndex())); + if(reader.current().is(')')) + return new Token(reader.consume().toString(), Token.Type.GROUP_END, new Position(reader.getLine(), reader.getIndex())); + if(reader.current().is(';')) + return new Token(reader.consume().toString(), Token.Type.STATEMENT_END, new Position(reader.getLine(), reader.getIndex())); + if(reader.current().is(',')) + return new Token(reader.consume().toString(), Token.Type.SEPARATOR, new Position(reader.getLine(), reader.getIndex())); + if(reader.current().is('{')) + return new Token(reader.consume().toString(), Token.Type.BLOCK_BEGIN, new Position(reader.getLine(), reader.getIndex())); + if(reader.current().is('}')) + return new Token(reader.consume().toString(), Token.Type.BLOCK_END, new Position(reader.getLine(), reader.getIndex())); + if(reader.current().is('=')) + return new Token(reader.consume().toString(), Token.Type.ASSIGNMENT, new Position(reader.getLine(), reader.getIndex())); + if(reader.current().is('+')) + return new Token(reader.consume().toString(), Token.Type.ADDITION_OPERATOR, new Position(reader.getLine(), reader.getIndex())); + if(reader.current().is('-')) + return new Token(reader.consume().toString(), Token.Type.SUBTRACTION_OPERATOR, + new Position(reader.getLine(), reader.getIndex())); + if(reader.current().is('*')) + return new Token(reader.consume().toString(), Token.Type.MULTIPLICATION_OPERATOR, + new Position(reader.getLine(), reader.getIndex())); + if(reader.current().is('/')) + return new Token(reader.consume().toString(), Token.Type.DIVISION_OPERATOR, new Position(reader.getLine(), reader.getIndex())); + if(reader.current().is('%')) + return new Token(reader.consume().toString(), Token.Type.MODULO_OPERATOR, new Position(reader.getLine(), reader.getIndex())); + if(reader.current().is('!')) + return new Token(reader.consume().toString(), Token.Type.BOOLEAN_NOT, new Position(reader.getLine(), reader.getIndex())); + + StringBuilder token = new StringBuilder(); + while(!reader.current().isEOF() && !isSyntaxSignificant(reader.current().getCharacter())) { + Char c = reader.consume(); + if(c.isWhitespace()) break; + token.append(c); + } + + String tokenString = token.toString(); + + if(tokenString.equals("true")) + return new Token(tokenString, Token.Type.BOOLEAN, new Position(reader.getLine(), reader.getIndex())); + if(tokenString.equals("false")) + return new Token(tokenString, Token.Type.BOOLEAN, new Position(reader.getLine(), reader.getIndex())); + + if(tokenString.equals("num")) + return new Token(tokenString, Token.Type.NUMBER_VARIABLE, new Position(reader.getLine(), reader.getIndex())); + if(tokenString.equals("str")) + return new Token(tokenString, Token.Type.STRING_VARIABLE, new Position(reader.getLine(), reader.getIndex())); + if(tokenString.equals("bool")) + return new Token(tokenString, Token.Type.BOOLEAN_VARIABLE, new Position(reader.getLine(), reader.getIndex())); + + if(tokenString.equals("if")) + return new Token(tokenString, Token.Type.IF_STATEMENT, new Position(reader.getLine(), reader.getIndex())); + if(tokenString.equals("else")) + return new Token(tokenString, Token.Type.ELSE, new Position(reader.getLine(), reader.getIndex())); + if(tokenString.equals("while")) + return new Token(tokenString, Token.Type.WHILE_LOOP, new Position(reader.getLine(), reader.getIndex())); + if(tokenString.equals("for")) + return new Token(tokenString, Token.Type.FOR_LOOP, new Position(reader.getLine(), reader.getIndex())); + + if(tokenString.equals("return")) + return new Token(tokenString, Token.Type.RETURN, new Position(reader.getLine(), reader.getIndex())); + if(tokenString.equals("continue")) + return new Token(tokenString, Token.Type.CONTINUE, new Position(reader.getLine(), reader.getIndex())); + if(tokenString.equals("break")) + return new Token(tokenString, Token.Type.BREAK, new Position(reader.getLine(), reader.getIndex())); + if(tokenString.equals("fail")) + return new Token(tokenString, Token.Type.FAIL, new Position(reader.getLine(), reader.getIndex())); + + return new Token(tokenString, Token.Type.IDENTIFIER, new Position(reader.getLine(), reader.getIndex())); + } + + private void skipLine() { + while(!reader.current().isEOF() && !reader.current().isNewLine()) reader.consume(); + consumeWhitespace(); + } + + private void consumeWhitespace() { + while(!reader.current().isEOF() && reader.current().isWhitespace()) reader.consume(); // Consume whitespace. + } + + private void skipTo(String s) throws EOFException { + Position begin = new Position(reader.getLine(), reader.getIndex()); + while(!reader.current().isEOF()) { + if(reader.matches(s, true)) { + consumeWhitespace(); + return; + } + reader.consume(); + } + throw new EOFException("No end of expression found.", begin); + } + + /** + * Whether this {@code Tokenizer} contains additional tokens. + * + * @return {@code true} if more tokens are present, otherwise {@code false} + */ + public boolean hasNext() { + return !(current == null); + } + + private boolean isNumberLike() { + return reader.current().isDigit() + || reader.current().is('_', '.', 'E'); + } + + private boolean isNumberStart() { + return reader.current().isDigit() + || reader.current().is('.') && reader.next(1).isDigit(); + } + + public boolean isSyntaxSignificant(char c) { + return syntaxSignificant.contains(c); + } + +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/exceptions/EOFException.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/exceptions/EOFException.java new file mode 100644 index 000000000..8de70c536 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/exceptions/EOFException.java @@ -0,0 +1,17 @@ +package com.dfsek.terra.addons.terrascript.tokenizer.exceptions; + +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class EOFException extends TokenizerException { + + private static final long serialVersionUID = 3980047409902809440L; + + public EOFException(String message, Position position) { + super(message, position); + } + + public EOFException(String message, Position position, Throwable cause) { + super(message, position, cause); + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/exceptions/FormatException.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/exceptions/FormatException.java new file mode 100644 index 000000000..5ddef18b9 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/exceptions/FormatException.java @@ -0,0 +1,17 @@ +package com.dfsek.terra.addons.terrascript.tokenizer.exceptions; + +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class FormatException extends TokenizerException { + + private static final long serialVersionUID = -791308012940744455L; + + public FormatException(String message, Position position) { + super(message, position); + } + + public FormatException(String message, Position position, Throwable cause) { + super(message, position, cause); + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/exceptions/TokenizerException.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/exceptions/TokenizerException.java new file mode 100644 index 000000000..6798b10ab --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/tokenizer/exceptions/TokenizerException.java @@ -0,0 +1,18 @@ +package com.dfsek.terra.addons.terrascript.tokenizer.exceptions; + +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public abstract class TokenizerException extends ParseException { + + private static final long serialVersionUID = 2792384010083575420L; + + public TokenizerException(String message, Position position) { + super(message, position); + } + + public TokenizerException(String message, Position position, Throwable cause) { + super(message, position, cause); + } +} diff --git a/common/addons/structure-terrascript-loader/src/test/java/structure/LookaheadTest.java b/common/addons/structure-terrascript-loader/src/test/java/structure/LookaheadTest.java new file mode 100644 index 000000000..5b033f8e8 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/test/java/structure/LookaheadTest.java @@ -0,0 +1,22 @@ +package structure; + +import org.junit.jupiter.api.Test; + +import java.io.StringReader; + +import com.dfsek.terra.addons.terrascript.tokenizer.Lookahead; + + +public class LookaheadTest { + @Test + public void lookahead() { + Lookahead lookahead = new Lookahead(new StringReader("Test string...")); + + for(int i = 0; lookahead.next(i) != null; i++) { + System.out.print(lookahead.next(i).getCharacter()); + } + while(lookahead.next(0) != null) { + System.out.print(lookahead.consume().getCharacter()); + } + } +} diff --git a/common/addons/structure-terrascript-loader/src/test/java/structure/ParserTest.java b/common/addons/structure-terrascript-loader/src/test/java/structure/ParserTest.java new file mode 100644 index 000000000..d9147d684 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/test/java/structure/ParserTest.java @@ -0,0 +1,89 @@ +package structure; + + +import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.dfsek.terra.addons.terrascript.parser.Parser; +import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; +import com.dfsek.terra.addons.terrascript.parser.lang.Block; +import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; +import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; +import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.addons.terrascript.parser.lang.variables.Variable; +import com.dfsek.terra.addons.terrascript.tokenizer.Position; + + +public class ParserTest { + @Test + public void parse() throws IOException, ParseException { + Parser parser = new Parser(IOUtils.toString(getClass().getResourceAsStream("/test.tesf"), Charset.defaultCharset())); + + parser.registerFunction("test", new FunctionBuilder() { + @Override + public Test1 build(List> argumentList, Position position) { + return new Test1(argumentList.get(0), argumentList.get(1), position); + } + + @Override + public int argNumber() { + return 2; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + return switch(position) { + case 0 -> Returnable.ReturnType.STRING; + case 1 -> Returnable.ReturnType.NUMBER; + default -> null; + }; + } + + }); + + long l = System.nanoTime(); + Block block = parser.parse(); + long t = System.nanoTime() - l; + System.out.println("Took " + (double) t / 1000000); + + block.apply(null, new HashMap<>()); + + block.apply(null, new HashMap<>()); + } + + private static class Test1 implements Function { + private final Returnable a; + private final Returnable b; + private final Position position; + + public Test1(Returnable a, Returnable b, Position position) { + this.a = a; + this.b = b; + this.position = position; + } + + @Override + public Void apply(ImplementationArguments implementationArguments, Map> variableMap) { + System.out.println("string: " + a.apply(implementationArguments, variableMap) + ", double: " + + b.apply(implementationArguments, variableMap)); + return null; + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } + } +} diff --git a/common/addons/structure-terrascript-loader/src/test/resources/test.tesf b/common/addons/structure-terrascript-loader/src/test/resources/test.tesf new file mode 100644 index 000000000..8bcce7177 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/test/resources/test.tesf @@ -0,0 +1,97 @@ +bool thing1 = 2 > (2+2) || false; + +if(2 > 2 || 3 + 4 <= 2 && 4 + 5 > 2 / 3) { + test("ok", 2); +} + +test("minecraft:green_w" + "ool", (2 * (3+1) * (2 * (1+1)))); +// + +num testVar = 3.4; +bool boolean = true; +str stringVar = "hello!"; + +num precedence = 3 + 2 * 2 + 3; +test("precedence: " + precedence, 2); +num precedence2 = 3 * 2 + 2 * 3; +test("precedence 2: " + precedence2, 2); + +bool iftest = false; + +bool truetest = false; + +num iterator = 0; +num thing = 4 - 2-2+2-2+2; +test("4 - 2 = " + thing, 2); + +thing = -2; +test("-2 = " + thing, 2); +thing = -thing; +test("--2 = " + thing, 2); + + + +for(num i = 0; i < 5; i = i + 1) { + test("i = " + i, iterator); + if(i > 1 + 1) { + test("more than 2", iterator); + continue; + } +} + +for(num i = 0; i < 5; i = i + 1) { + test("i = " + i, iterator); +} + +for(num j = 0; j < 5; j = j + 1) test("single statement j = " + j, iterator); + +if(4 + 2 == 2 + 4) { + test("new thing " + 2, iterator); +} + +while(iterator < 5) { + test("always, even after " + 2, iterator); + iterator = iterator + 1; + if(iterator > 2) { + continue; + } + test("not after " + 2, iterator); +} + +if(true) test("single statement" + 2, iterator); +else if(true) test("another single statement" + 2, iterator); + +if(true) { + test("true!" + 2, iterator); +} else { + test("false!" + 2, iterator); + } + +if(false) { + test("true!" + 2, iterator); +} else { + test("false!" + 2, iterator); +} + +if(false) { + test("true again!" + 2, iterator); +} else if(true == true) { + test("false again!" + 2, iterator); +} else { + test("not logged!" + 2, iterator); +} + + + +// comment + +/* +fsdfsd +*/ + +test("fdsgdf" + 2, 1 + testVar); + +if(true && !(boolean && false) && true) { + num scopedVar = 2; + test("if statement" + 2 + stringVar, 1 + testVar + scopedVar); +} \ No newline at end of file