Re-add terrascript v1 module

This commit is contained in:
Astrash
2023-10-23 13:10:32 +11:00
parent d139019e01
commit 433d695e8b
101 changed files with 5512 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020-2021 Polyhedral Development
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,4 @@
# structure-terrascript-loader
Implements the TerraScript structure scripting language, and loads all `*.tesf`
files into the Structure registry.

View File

@@ -0,0 +1,15 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
version = version("1.1.0")
dependencies {
api("commons-io:commons-io:2.7")
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
implementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
testImplementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
}
tasks.named<ShadowJar>("shadowJar") {
relocate("org.apache.commons", "com.dfsek.terra.addons.terrascript.lib.commons")
relocate("net.jafama", "com.dfsek.terra.addons.terrascript.lib.jafama")
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder;
import com.dfsek.terra.addons.terrascript.script.StructureScript;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.addon.BaseAddon;
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;
public class TerraScriptAddon implements AddonInitializer {
@Inject
private Platform platform;
@Inject
private BaseAddon addon;
@Override
public void initialize() {
platform.getEventManager()
.getHandler(FunctionalEventHandler.class)
.register(addon, ConfigPackPreLoadEvent.class)
.then(event -> {
CheckedRegistry<Structure> structureRegistry = event.getPack().getOrCreateRegistry(Structure.class);
CheckedRegistry<LootTable> lootRegistry = event.getPack().getOrCreateRegistry(LootTable.class);
event.getPack().getLoader().open("", ".tesf").thenEntries(
entries ->
entries.stream()
.parallel()
.map(entry -> {
try {
String id = StringUtil.fileName(entry.getKey());
return new StructureScript(entry.getValue(),
addon.key(id),
platform,
structureRegistry,
lootRegistry,
event.getPack().getOrCreateRegistry(FunctionBuilder.class));
} catch(ParseException e) {
throw new RuntimeException("Failed to load script \"" + entry.getKey() + "\"", e);
}
})
.toList()
.forEach(structureRegistry::register))
.close();
})
.priority(100)
.failThrough();
}
}

View File

@@ -0,0 +1,468 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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.Executable;
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.Returnable.ReturnType;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope.ScopeBuilder;
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.assign.BoolAssignmentNode;
import com.dfsek.terra.addons.terrascript.parser.lang.variables.assign.NumAssignmentNode;
import com.dfsek.terra.addons.terrascript.parser.lang.variables.assign.StrAssignmentNode;
import com.dfsek.terra.addons.terrascript.parser.lang.variables.assign.VariableAssignmentNode;
import com.dfsek.terra.addons.terrascript.parser.lang.variables.reference.BoolVariableReferenceNode;
import com.dfsek.terra.addons.terrascript.parser.lang.variables.reference.NumVariableReferenceNode;
import com.dfsek.terra.addons.terrascript.parser.lang.variables.reference.StrVariableReferenceNode;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.addons.terrascript.tokenizer.Token;
import com.dfsek.terra.addons.terrascript.tokenizer.Tokenizer;
import com.dfsek.terra.api.util.generic.pair.Pair;
@SuppressWarnings("unchecked")
public class Parser {
private final String data;
private final Map<String, FunctionBuilder<? extends Function<?>>> functions = new HashMap<>();
private final List<String> ignoredFunctions = new ArrayList<>();
public Parser(String data) {
this.data = data;
}
public Parser registerFunction(String name, FunctionBuilder<? extends Function<?>> 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 Executable parse() {
ScopeBuilder scopeBuilder = new ScopeBuilder();
return new Executable(parseBlock(new Tokenizer(data), false, scopeBuilder), scopeBuilder);
}
private Keyword<?> parseLoopLike(Tokenizer tokens, boolean loop, ScopeBuilder scopeBuilder) 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, identifier.getPosition(), scopeBuilder);
case IF_STATEMENT -> parseIfStatement(tokens, identifier.getPosition(), loop, scopeBuilder);
case WHILE_LOOP -> parseWhileLoop(tokens, identifier.getPosition(), scopeBuilder);
default -> throw new UnsupportedOperationException(
"Unknown keyword " + identifier.getContent() + ": " + identifier.getPosition());
};
}
private WhileKeyword parseWhileLoop(Tokenizer tokens, Position start, ScopeBuilder scopeBuilder) {
Returnable<?> first = parseExpression(tokens, true, scopeBuilder);
ParserUtil.checkReturnType(first, Returnable.ReturnType.BOOLEAN);
ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_END);
return new WhileKeyword(parseStatementBlock(tokens, true, scopeBuilder), (Returnable<Boolean>) first, start); // While loop
}
private IfKeyword parseIfStatement(Tokenizer tokens, Position start, boolean loop, ScopeBuilder scopeBuilder) {
Returnable<?> condition = parseExpression(tokens, true, scopeBuilder);
ParserUtil.checkReturnType(condition, Returnable.ReturnType.BOOLEAN);
ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_END);
Block elseBlock = null;
Block statement = parseStatementBlock(tokens, loop, scopeBuilder);
List<Pair<Returnable<Boolean>, 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, scopeBuilder);
ParserUtil.checkReturnType(elseCondition, Returnable.ReturnType.BOOLEAN);
elseIf.add(Pair.of((Returnable<Boolean>) elseCondition, parseStatementBlock(tokens, loop, scopeBuilder)));
} else {
elseBlock = parseStatementBlock(tokens, loop, scopeBuilder);
break; // Else must be last.
}
}
return new IfKeyword(statement, (Returnable<Boolean>) condition, elseIf, elseBlock, start); // If statement
}
private Block parseStatementBlock(Tokenizer tokens, boolean loop, ScopeBuilder scopeBuilder) {
if(tokens.get().getType().equals(Token.Type.BLOCK_BEGIN)) {
ParserUtil.checkType(tokens.consume(), Token.Type.BLOCK_BEGIN);
Block block = parseBlock(tokens, loop, scopeBuilder);
ParserUtil.checkType(tokens.consume(), Token.Type.BLOCK_END);
return block;
} else {
Position position = tokens.get().getPosition();
Block block = new Block(Collections.singletonList(parseItem(tokens, loop, scopeBuilder)), position);
ParserUtil.checkType(tokens.consume(), Token.Type.STATEMENT_END);
return block;
}
}
private ForKeyword parseForLoop(Tokenizer tokens, Position start, ScopeBuilder scopeBuilder) {
scopeBuilder = scopeBuilder.sub(); // 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()) {
VariableAssignmentNode<?> forVar = parseVariableDeclaration(tokens, scopeBuilder);
Token name = tokens.get();
if(functions.containsKey(name.getContent()) || scopeBuilder.contains(name.getContent()))
throw new ParseException(name.getContent() + " is already defined in this scope", name.getPosition());
initializer = forVar;
} else initializer = parseExpression(tokens, true, scopeBuilder);
ParserUtil.checkType(tokens.consume(), Token.Type.STATEMENT_END);
Returnable<?> conditional = parseExpression(tokens, true, scopeBuilder);
ParserUtil.checkReturnType(conditional, Returnable.ReturnType.BOOLEAN);
ParserUtil.checkType(tokens.consume(), Token.Type.STATEMENT_END);
Item<?> incrementer;
Token token = tokens.get();
if(scopeBuilder.contains(token.getContent())) { // Assume variable assignment
incrementer = parseAssignment(tokens, scopeBuilder);
} else incrementer = parseFunction(tokens, true, scopeBuilder);
ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_END);
return new ForKeyword(parseStatementBlock(tokens, true, scopeBuilder), initializer, (Returnable<Boolean>) conditional, incrementer,
start);
}
private Returnable<?> parseExpression(Tokenizer tokens, boolean full, ScopeBuilder scopeBuilder) {
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, scopeBuilder);
} else {
if(functions.containsKey(id.getContent()))
expression = parseFunction(tokens, false, scopeBuilder);
else if(scopeBuilder.contains(id.getContent())) {
ParserUtil.checkType(tokens.consume(), Token.Type.IDENTIFIER);
String varId = id.getContent();
ReturnType varType = scopeBuilder.getType(varId);
expression = switch(varType) {
case NUMBER -> new NumVariableReferenceNode(id.getPosition(), varType, scopeBuilder.getIndex(varId));
case STRING -> new StrVariableReferenceNode(id.getPosition(), varType, scopeBuilder.getIndex(varId));
case BOOLEAN -> new BoolVariableReferenceNode(id.getPosition(), varType, scopeBuilder.getIndex(varId));
default -> throw new ParseException("Illegal type for variable reference: " + varType, id.getPosition());
};
} 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<Boolean>) expression, expression.getPosition());
} else if(negate) {
ParserUtil.checkReturnType(expression, Returnable.ReturnType.NUMBER);
expression = new NegationOperation((Returnable<Number>) expression, expression.getPosition());
}
if(full && tokens.get().isBinaryOperator()) { // Parse binary operations
return parseBinaryOperation(expression, tokens, scopeBuilder);
}
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, ScopeBuilder scopeBuilder) {
ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_BEGIN);
Returnable<?> expression = parseExpression(tokens, true, scopeBuilder); // 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,
ScopeBuilder scopeBuilder) {
Token binaryOperator = tokens.consume();
ParserUtil.checkBinaryOperator(binaryOperator);
Returnable<?> right = parseExpression(tokens, false, scopeBuilder);
Token other = tokens.get();
if(ParserUtil.hasPrecedence(binaryOperator.getType(), other.getType())) {
return assemble(left, parseBinaryOperation(right, tokens, scopeBuilder), binaryOperator);
} else if(other.isBinaryOperator()) {
return parseBinaryOperation(assemble(left, right, binaryOperator), tokens, scopeBuilder);
}
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<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition());
}
return new ConcatenationOperation((Returnable<Object>) left, (Returnable<Object>) right, binaryOperator.getPosition());
case SUBTRACTION_OPERATOR:
return new SubtractionOperation((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition());
case MULTIPLICATION_OPERATOR:
return new MultiplicationOperation((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition());
case DIVISION_OPERATOR:
return new DivisionOperation((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition());
case EQUALS_OPERATOR:
return new EqualsStatement((Returnable<Object>) left, (Returnable<Object>) right, binaryOperator.getPosition());
case NOT_EQUALS_OPERATOR:
return new NotEqualsStatement((Returnable<Object>) left, (Returnable<Object>) right, binaryOperator.getPosition());
case GREATER_THAN_OPERATOR:
return new GreaterThanStatement((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition());
case LESS_THAN_OPERATOR:
return new LessThanStatement((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition());
case GREATER_THAN_OR_EQUALS_OPERATOR:
return new GreaterOrEqualsThanStatement((Returnable<Number>) left, (Returnable<Number>) right,
binaryOperator.getPosition());
case LESS_THAN_OR_EQUALS_OPERATOR:
return new LessThanOrEqualsStatement((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition());
case BOOLEAN_AND:
return new BooleanAndOperation((Returnable<Boolean>) left, (Returnable<Boolean>) right, binaryOperator.getPosition());
case BOOLEAN_OR:
return new BooleanOrOperation((Returnable<Boolean>) left, (Returnable<Boolean>) right, binaryOperator.getPosition());
case MODULO_OPERATOR:
return new ModuloOperation((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition());
default:
throw new UnsupportedOperationException("Unsupported binary operator: " + binaryOperator.getType());
}
}
private VariableAssignmentNode<?> parseVariableDeclaration(Tokenizer tokens, ScopeBuilder scopeBuilder) {
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()) || scopeBuilder.contains(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, scopeBuilder);
ParserUtil.checkReturnType(value, returnType);
String id = identifier.getContent();
return switch(value.returnType()) {
case NUMBER -> new NumAssignmentNode((Returnable<Number>) value, identifier.getPosition(), scopeBuilder.num(id));
case STRING -> new StrAssignmentNode((Returnable<String>) value, identifier.getPosition(), scopeBuilder.str(id));
case BOOLEAN -> new BoolAssignmentNode((Returnable<Boolean>) value, identifier.getPosition(), scopeBuilder.bool(id));
default -> throw new ParseException("Illegal type for variable declaration: " + type, value.getPosition());
};
}
private Block parseBlock(Tokenizer tokens, boolean loop, ScopeBuilder scopeBuilder) {
List<Item<?>> parsedItems = new ArrayList<>();
scopeBuilder = scopeBuilder.sub();
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, loop, scopeBuilder);
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, boolean loop, ScopeBuilder scopeBuilder) {
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, loop, scopeBuilder);
} else if(token.isIdentifier()) { // Parse identifiers
if(scopeBuilder.contains(token.getContent())) { // Assume variable assignment
return parseAssignment(tokens, scopeBuilder);
} else return parseFunction(tokens, true, scopeBuilder);
} else if(token.isVariableDeclaration()) {
return parseVariableDeclaration(tokens, scopeBuilder);
} 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 VariableAssignmentNode<?> parseAssignment(Tokenizer tokens, ScopeBuilder scopeBuilder) {
Token identifier = tokens.consume();
ParserUtil.checkType(identifier, Token.Type.IDENTIFIER);
ParserUtil.checkType(tokens.consume(), Token.Type.ASSIGNMENT);
Returnable<?> value = parseExpression(tokens, true, scopeBuilder);
String id = identifier.getContent();
ParserUtil.checkReturnType(value, scopeBuilder.getType(id));
ReturnType type = value.returnType();
return switch(type) {
case NUMBER -> new NumAssignmentNode((Returnable<Number>) value, identifier.getPosition(), scopeBuilder.getIndex(id));
case STRING -> new StrAssignmentNode((Returnable<String>) value, identifier.getPosition(), scopeBuilder.getIndex(id));
case BOOLEAN -> new BoolAssignmentNode((Returnable<Boolean>) value, identifier.getPosition(), scopeBuilder.getIndex(id));
default -> throw new ParseException("Illegal type for variable assignment: " + type, value.getPosition());
};
}
private Function<?> parseFunction(Tokenizer tokens, boolean fullStatement, ScopeBuilder scopeBuilder) {
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<Returnable<?>> args = getArgs(tokens, scopeBuilder); // 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<Returnable<?>> getArgs(Tokenizer tokens, ScopeBuilder scopeBuilder) {
List<Returnable<?>> args = new ArrayList<>();
while(!tokens.get().getType().equals(Token.Type.GROUP_END)) {
args.add(parseExpression(tokens, true, scopeBuilder));
ParserUtil.checkType(tokens.get(), Token.Type.SEPARATOR, Token.Type.GROUP_END);
if(tokens.get().getType().equals(Token.Type.SEPARATOR)) tokens.consume();
}
return args;
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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<Token.Type, Map<Token.Type, Boolean>> PRECEDENCE = new HashMap<>(); // If second has precedence, true.
private static final List<Token.Type> 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<Token.Type> 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<Token.Type, Boolean> 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<Token.Type, Boolean> numericBoolean = new HashMap<>();
ARITHMETIC.forEach(op -> numericBoolean.put(op, true)); // Numbers before comparison
COMPARISON.forEach(op -> PRECEDENCE.put(op, numericBoolean));
Map<Token.Type, Boolean> 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<Token.Type, Boolean> pre = PRECEDENCE.get(first);
if(!pre.containsKey(second)) return false;
return pre.get(second);
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.exceptions;
import java.io.Serial;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ParseException extends RuntimeException {
@Serial
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;
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang;
import java.util.List;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class Block implements Item<Block.ReturnInfo<?>> {
private final List<Item<?>> items;
private final Position position;
public Block(List<Item<?>> items, Position position) {
this.items = items;
this.position = position;
}
@Override
public ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) {
for(Item<?> item : items) {
Object result = item.apply(implementationArguments, scope);
if(result instanceof ReturnInfo<?> level) {
if(!level.getLevel().equals(ReturnLevel.NONE)) return level;
}
}
return new ReturnInfo<>(ReturnLevel.NONE, null);
}
@Override
public Position getPosition() {
return position;
}
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<T> {
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;
}
}
}

View File

@@ -0,0 +1,19 @@
package com.dfsek.terra.addons.terrascript.parser.lang;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope.ScopeBuilder;
public class Executable {
private final Block script;
private final ThreadLocal<Scope> scope;
public Executable(Block script, ScopeBuilder scopeBuilder) {
this.script = script;
this.scope = ThreadLocal.withInitial(scopeBuilder::build);
}
public boolean execute(ImplementationArguments arguments) {
return script.apply(arguments, scope.get()).getLevel() != Block.ReturnLevel.FAIL;
}
}

View File

@@ -0,0 +1,14 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang;
/**
* Arguments passed to {@link Item}s by the implementation
*/
public interface ImplementationArguments {
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public interface Item<T> {
T apply(ImplementationArguments implementationArguments, Scope scope);
default double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
throw new UnsupportedOperationException("Cannot apply " + this + " as double");
}
default boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
throw new UnsupportedOperationException("Cannot apply " + this + " as double");
}
Position getPosition();
}

View File

@@ -0,0 +1,11 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang;
public interface Keyword<T> extends Returnable<T> {
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang;
public interface Returnable<T> extends Item<T> {
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;
}
}
}

View File

@@ -0,0 +1,138 @@
package com.dfsek.terra.addons.terrascript.parser.lang;
import net.jafama.FastMath;
import java.util.HashMap;
import java.util.Map;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable.ReturnType;
import com.dfsek.terra.api.util.generic.pair.Pair;
public class Scope {
private final double[] num;
private final boolean[] bool;
private final String[] str;
private Scope(int numSize, int boolSize, int strSize) {
this.num = new double[numSize];
this.bool = new boolean[boolSize];
this.str = new String[strSize];
}
public double getNum(int index) {
return num[index];
}
public boolean getBool(int index) {
return bool[index];
}
public String getStr(int index) {
return str[index];
}
public void setNum(int index, double value) {
num[index] = value;
}
public void setBool(int index, boolean value) {
bool[index] = value;
}
public void setStr(int index, String value) {
str[index] = value;
}
public static final class ScopeBuilder {
private final Map<String, Pair<Integer, ReturnType>> indices;
private int numSize, boolSize, strSize = 0;
private ScopeBuilder parent;
public ScopeBuilder() {
this.indices = new HashMap<>();
}
private ScopeBuilder(ScopeBuilder parent) {
this.parent = parent;
this.numSize = parent.numSize;
this.boolSize = parent.boolSize;
this.strSize = parent.strSize;
this.indices = new HashMap<>(parent.indices);
}
public Scope build() {
return new Scope(numSize, boolSize, strSize);
}
public ScopeBuilder sub() {
return new ScopeBuilder(this);
}
private String check(String id) {
if(indices.containsKey(id)) {
throw new IllegalArgumentException("Variable with ID " + id + " already registered.");
}
return id;
}
public int num(String id) {
int num = numSize;
indices.put(check(id), Pair.of(num, ReturnType.NUMBER));
numSize++;
updateNumSize(numSize);
return num;
}
public int str(String id) {
int str = strSize;
indices.put(check(id), Pair.of(str, ReturnType.STRING));
strSize++;
updateStrSize(strSize);
return str;
}
public int bool(String id) {
int bool = boolSize;
indices.put(check(id), Pair.of(bool, ReturnType.BOOLEAN));
boolSize++;
updateBoolSize(boolSize);
return bool;
}
private void updateBoolSize(int size) {
this.boolSize = FastMath.max(boolSize, size);
if(parent != null) {
parent.updateBoolSize(size);
}
}
private void updateNumSize(int size) {
this.numSize = FastMath.max(numSize, size);
if(parent != null) {
parent.updateNumSize(size);
}
}
private void updateStrSize(int size) {
this.strSize = FastMath.max(strSize, size);
if(parent != null) {
parent.updateStrSize(size);
}
}
public int getIndex(String id) {
return indices.get(id).getLeft();
}
public ReturnType getType(String id) {
return indices.get(id).getRight();
}
public boolean contains(String id) {
return indices.containsKey(id);
}
}
}

View File

@@ -0,0 +1,11 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang;
public interface Statement extends Item<Boolean> {
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.constants;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BooleanConstant extends ConstantExpression<Boolean> {
private final boolean constant;
public BooleanConstant(Boolean constant, Position position) {
super(constant, position);
this.constant = constant;
}
@Override
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
return constant;
}
@Override
public ReturnType returnType() {
return ReturnType.BOOLEAN;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.constants;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public abstract class ConstantExpression<T> implements Returnable<T> {
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, Scope scope) {
return constant;
}
@Override
public Position getPosition() {
return position;
}
public T getConstant() {
return constant;
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.constants;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class NumericConstant extends ConstantExpression<Number> {
private final double constant;
public NumericConstant(Number constant, Position position) {
super(constant, position);
this.constant = constant.doubleValue();
}
@Override
public double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return constant;
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.NUMBER;
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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<String> {
public StringConstant(String constant, Position position) {
super(constant, position);
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.STRING;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.functions;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public interface Function<T> extends Returnable<T> {
Function<?> NULL = new Function<>() {
@Override
public ReturnType returnType() {
return null;
}
@Override
public Object apply(ImplementationArguments implementationArguments, Scope scope) {
return null;
}
@Override
public Position getPosition() {
return null;
}
};
@Override
default double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return ((Number) apply(implementationArguments, scope)).doubleValue();
}
@Override
default boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
return (Boolean) apply(implementationArguments, scope);
}
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.functions;
import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public interface FunctionBuilder<T extends Function<?>> {
T build(List<Returnable<?>> argumentList, Position position);
int argNumber();
Returnable.ReturnType getArgument(int position);
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BreakKeyword implements Keyword<Block.ReturnInfo<?>> {
private final Position position;
public BreakKeyword(Position position) {
this.position = position;
}
@Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) {
return new Block.ReturnInfo<>(Block.ReturnLevel.BREAK, null);
}
@Override
public Position getPosition() {
return position;
}
@Override
public ReturnType returnType() {
return ReturnType.VOID;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ContinueKeyword implements Keyword<Block.ReturnInfo<?>> {
private final Position position;
public ContinueKeyword(Position position) {
this.position = position;
}
@Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) {
return new Block.ReturnInfo<>(Block.ReturnLevel.CONTINUE, null);
}
@Override
public Position getPosition() {
return position;
}
@Override
public ReturnType returnType() {
return ReturnType.VOID;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class FailKeyword implements Keyword<Block.ReturnInfo<?>> {
private final Position position;
public FailKeyword(Position position) {
this.position = position;
}
@Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) {
return new Block.ReturnInfo<>(Block.ReturnLevel.FAIL, null);
}
@Override
public Position getPosition() {
return position;
}
@Override
public ReturnType returnType() {
return ReturnType.VOID;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ReturnKeyword implements Keyword<Block.ReturnInfo<?>> {
private final Position position;
public ReturnKeyword(Position position) {
this.position = position;
}
@Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) {
return new Block.ReturnInfo<>(Block.ReturnLevel.RETURN, null);
}
@Override
public Position getPosition() {
return position;
}
@Override
public ReturnType returnType() {
return ReturnType.VOID;
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ForKeyword implements Keyword<Block.ReturnInfo<?>> {
private final Block conditional;
private final Item<?> initializer;
private final Returnable<Boolean> statement;
private final Item<?> incrementer;
private final Position position;
public ForKeyword(Block conditional, Item<?> initializer, Returnable<Boolean> 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, Scope scope) {
for(initializer.apply(implementationArguments, scope);
statement.apply(implementationArguments, scope);
incrementer.apply(implementationArguments, scope)) {
Block.ReturnInfo<?> level = conditional.apply(implementationArguments, scope);
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;
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike;
import org.jetbrains.annotations.Nullable;
import java.util.List;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.util.generic.pair.Pair;
public class IfKeyword implements Keyword<Block.ReturnInfo<?>> {
private final Block conditional;
private final Returnable<Boolean> statement;
private final Position position;
private final List<Pair<Returnable<Boolean>, Block>> elseIf;
private final Block elseBlock;
public IfKeyword(Block conditional, Returnable<Boolean> statement, List<Pair<Returnable<Boolean>, 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, Scope scope) {
if(statement.apply(implementationArguments, scope)) return conditional.apply(implementationArguments, scope);
else {
for(Pair<Returnable<Boolean>, Block> pair : elseIf) {
if(pair.getLeft().apply(implementationArguments, scope)) {
return pair.getRight().apply(implementationArguments, scope);
}
}
if(elseBlock != null) return elseBlock.apply(implementationArguments, scope);
}
return new Block.ReturnInfo<>(Block.ReturnLevel.NONE, null);
}
@Override
public Position getPosition() {
return position;
}
@Override
public ReturnType returnType() {
return ReturnType.VOID;
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class WhileKeyword implements Keyword<Block.ReturnInfo<?>> {
private final Block conditional;
private final Returnable<Boolean> statement;
private final Position position;
public WhileKeyword(Block conditional, Returnable<Boolean> statement, Position position) {
this.conditional = conditional;
this.statement = statement;
this.position = position;
}
@Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) {
while(statement.apply(implementationArguments, scope)) {
Block.ReturnInfo<?> level = conditional.apply(implementationArguments, scope);
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;
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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 abstract class BinaryOperation<I, O> implements Returnable<O> {
protected final Returnable<I> left;
protected final Returnable<I> right;
private final Position start;
public BinaryOperation(Returnable<I> left, Returnable<I> right, Position start) {
this.left = left;
this.right = right;
this.start = start;
}
@Override
public Position getPosition() {
return start;
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.operations;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BooleanAndOperation extends BinaryOperation<Boolean, Boolean> {
public BooleanAndOperation(Returnable<Boolean> left, Returnable<Boolean> right, Position start) {
super(left, right, start);
}
@Override
public ReturnType returnType() {
return ReturnType.BOOLEAN;
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@Override
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
return left.applyBoolean(implementationArguments, scope) && right.applyBoolean(implementationArguments, scope);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.operations;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BooleanNotOperation extends UnaryOperation<Boolean> {
public BooleanNotOperation(Returnable<Boolean> input, Position position) {
super(input, position);
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@Override
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
return !input.applyBoolean(implementationArguments, scope);
}
@Override
public ReturnType returnType() {
return ReturnType.BOOLEAN;
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.operations;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BooleanOrOperation extends BinaryOperation<Boolean, Boolean> {
public BooleanOrOperation(Returnable<Boolean> left, Returnable<Boolean> right, Position start) {
super(left, right, start);
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@Override
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
return left.applyBoolean(implementationArguments, scope) || right.applyBoolean(implementationArguments, scope);
}
@Override
public ReturnType returnType() {
return ReturnType.BOOLEAN;
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.operations;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ConcatenationOperation extends BinaryOperation<Object, Object> {
public ConcatenationOperation(Returnable<Object> left, Returnable<Object> right, Position position) {
super(left, right, position);
}
private static String toString(Object object) {
String s = object.toString();
if(object instanceof Double) {
int l = s.length();
if(s.charAt(l - 2) == '.' && s.charAt(l - 1) == '0') {
s = s.substring(0, s.length() - 2);
}
}
return s;
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.STRING;
}
@Override
public Object apply(ImplementationArguments implementationArguments, Scope scope) {
return toString(left.apply(implementationArguments, scope)) + toString(right.apply(implementationArguments, scope));
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.operations;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class DivisionOperation extends BinaryOperation<Number, Number> {
public DivisionOperation(Returnable<Number> left, Returnable<Number> right, Position position) {
super(left, right, position);
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.NUMBER;
}
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope);
}
@Override
public double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) / right.applyDouble(implementationArguments, scope);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.operations;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ModuloOperation extends BinaryOperation<Number, Number> {
public ModuloOperation(Returnable<Number> left, Returnable<Number> right, Position start) {
super(left, right, start);
}
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope);
}
@Override
public double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) % right.applyDouble(implementationArguments, scope);
}
@Override
public ReturnType returnType() {
return ReturnType.NUMBER;
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.operations;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class MultiplicationOperation extends BinaryOperation<Number, Number> {
public MultiplicationOperation(Returnable<Number> left, Returnable<Number> right, Position position) {
super(left, right, position);
}
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope);
}
@Override
public double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) * right.applyDouble(implementationArguments, scope);
}
@Override
public ReturnType returnType() {
return ReturnType.NUMBER;
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.operations;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class NegationOperation extends UnaryOperation<Number> {
public NegationOperation(Returnable<Number> input, Position position) {
super(input, position);
}
@Override
public ReturnType returnType() {
return ReturnType.NUMBER;
}
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope);
}
@Override
public double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return -input.applyDouble(implementationArguments, scope);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.operations;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class NumberAdditionOperation extends BinaryOperation<Number, Number> {
public NumberAdditionOperation(Returnable<Number> left, Returnable<Number> right, Position position) {
super(left, right, position);
}
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope);
}
@Override
public double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) + right.applyDouble(implementationArguments, scope);
}
@Override
public ReturnType returnType() {
return ReturnType.NUMBER;
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.operations;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class SubtractionOperation extends BinaryOperation<Number, Number> {
public SubtractionOperation(Returnable<Number> left, Returnable<Number> right, Position position) {
super(left, right, position);
}
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope);
}
@Override
public double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) - right.applyDouble(implementationArguments, scope);
}
@Override
public ReturnType returnType() {
return ReturnType.NUMBER;
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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 abstract class UnaryOperation<T> implements Returnable<T> {
protected final Returnable<T> input;
private final Position position;
public UnaryOperation(Returnable<T> input, Position position) {
this.input = input;
this.position = position;
}
@Override
public Position getPosition() {
return position;
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements;
import net.jafama.FastMath;
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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import static com.dfsek.terra.api.util.MathUtil.EPSILON;
public class EqualsStatement extends BinaryOperation<Object, Boolean> {
public EqualsStatement(Returnable<Object> left, Returnable<Object> right, Position position) {
super(left, right, position);
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN;
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@Override
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
Object leftValue = left.apply(implementationArguments, scope);
Object rightValue = right.apply(implementationArguments, scope);
if(leftValue instanceof Number l && rightValue instanceof Number r) {
return FastMath.abs(l.doubleValue() - r.doubleValue()) <= EPSILON;
}
return leftValue.equals(rightValue);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements;
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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class GreaterOrEqualsThanStatement extends BinaryOperation<Number, Boolean> {
public GreaterOrEqualsThanStatement(Returnable<Number> left, Returnable<Number> right, Position position) {
super(left, right, position);
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN;
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@Override
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) >= right.applyDouble(implementationArguments, scope);
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements;
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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class GreaterThanStatement extends BinaryOperation<Number, Boolean> {
public GreaterThanStatement(Returnable<Number> left, Returnable<Number> right, Position position) {
super(left, right, position);
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@Override
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) > right.applyDouble(implementationArguments, scope);
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements;
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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class LessThanOrEqualsStatement extends BinaryOperation<Number, Boolean> {
public LessThanOrEqualsStatement(Returnable<Number> left, Returnable<Number> right, Position position) {
super(left, right, position);
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@Override
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) <= right.applyDouble(implementationArguments, scope);
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements;
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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class LessThanStatement extends BinaryOperation<Number, Boolean> {
public LessThanStatement(Returnable<Number> left, Returnable<Number> right, Position position) {
super(left, right, position);
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@Override
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) < right.applyDouble(implementationArguments, scope);
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements;
import net.jafama.FastMath;
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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import static com.dfsek.terra.api.util.MathUtil.EPSILON;
public class NotEqualsStatement extends BinaryOperation<Object, Boolean> {
public NotEqualsStatement(Returnable<Object> left, Returnable<Object> right, Position position) {
super(left, right, position);
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@Override
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
Object leftValue = left.apply(implementationArguments, scope);
Object rightValue = right.apply(implementationArguments, scope);
if(leftValue instanceof Number l && rightValue instanceof Number r) {
return FastMath.abs(l.doubleValue() - r.doubleValue()) > EPSILON;
}
return !leftValue.equals(rightValue);
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN;
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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<Boolean> {
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;
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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<Number> {
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;
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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<String> {
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;
}
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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> {
T getValue();
void setValue(T value);
Returnable.ReturnType getType();
Position getPosition();
}

View File

@@ -0,0 +1,25 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables.assign;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BoolAssignmentNode extends VariableAssignmentNode<Boolean> {
public BoolAssignmentNode(Returnable<Boolean> value, Position position, int index) {
super(value, position, index);
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@Override
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
boolean val = value.applyBoolean(implementationArguments, scope);
scope.setBool(index, val);
return val;
}
}

View File

@@ -0,0 +1,25 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables.assign;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class NumAssignmentNode extends VariableAssignmentNode<Number> {
public NumAssignmentNode(Returnable<Number> value, Position position, int index) {
super(value, position, index);
}
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope);
}
@Override
public double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
double val = value.applyDouble(implementationArguments, scope);
scope.setNum(index, val);
return val;
}
}

View File

@@ -0,0 +1,21 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables.assign;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class StrAssignmentNode extends VariableAssignmentNode<String> {
public StrAssignmentNode(Returnable<String> value, Position position, int index) {
super(value, position, index);
}
@Override
public String apply(ImplementationArguments implementationArguments, Scope scope) {
String val = value.apply(implementationArguments, scope);
scope.setStr(index, val);
return val;
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.variables.assign;
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 abstract class VariableAssignmentNode<T> implements Item<T> {
protected final Returnable<T> value;
protected final int index;
private final Position position;
public VariableAssignmentNode(Returnable<T> value, Position position, int index) {
this.value = value;
this.index = index;
this.position = position;
}
@Override
public Position getPosition() {
return position;
}
}

View File

@@ -0,0 +1,22 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables.reference;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BoolVariableReferenceNode extends VariableReferenceNode<Boolean> {
public BoolVariableReferenceNode(Position position, ReturnType type, int index) {
super(position, type, index);
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
return scope.getBool(index);
}
@Override
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
return scope.getBool(index);
}
}

View File

@@ -0,0 +1,22 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables.reference;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class NumVariableReferenceNode extends VariableReferenceNode<Number> {
public NumVariableReferenceNode(Position position, ReturnType type, int index) {
super(position, type, index);
}
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
return scope.getNum(index);
}
@Override
public double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return scope.getNum(index);
}
}

View File

@@ -0,0 +1,17 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables.reference;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class StrVariableReferenceNode extends VariableReferenceNode<String> {
public StrVariableReferenceNode(Position position, ReturnType type, int index) {
super(position, type, index);
}
@Override
public String apply(ImplementationArguments implementationArguments, Scope scope) {
return scope.getStr(index);
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.parser.lang.variables.reference;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public abstract class VariableReferenceNode<T> implements Returnable<T> {
protected final int index;
private final Position position;
private final ReturnType type;
public VariableReferenceNode(Position position, ReturnType type, int index) {
this.position = position;
this.type = type;
this.index = index;
}
@Override
public ReturnType returnType() {
return type;
}
@Override
public Position getPosition() {
return position;
}
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script;
import net.jafama.FastMath;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Random;
import com.dfsek.terra.addons.terrascript.parser.Parser;
import com.dfsek.terra.addons.terrascript.parser.lang.Executable;
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.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.registry.Registry;
import com.dfsek.terra.api.registry.key.Keyed;
import com.dfsek.terra.api.registry.key.RegistryKey;
import com.dfsek.terra.api.structure.LootTable;
import com.dfsek.terra.api.structure.Structure;
import com.dfsek.terra.api.util.Rotation;
import com.dfsek.terra.api.util.vector.Vector3Int;
import com.dfsek.terra.api.world.WritableWorld;
public class StructureScript implements Structure, Keyed<StructureScript> {
private static final Logger LOGGER = LoggerFactory.getLogger(StructureScript.class);
private final Executable block;
private final RegistryKey id;
private final String profile;
private final Platform platform;
@SuppressWarnings("rawtypes")
public StructureScript(InputStream inputStream, RegistryKey id, Platform platform, Registry<Structure> registry,
Registry<LootTable> lootRegistry,
Registry<FunctionBuilder> functionRegistry) {
Parser parser;
try {
parser = new Parser(IOUtils.toString(inputStream, Charset.defaultCharset()));
} catch(IOException e) {
throw new RuntimeException(e);
}
this.id = id;
this.profile = "terrascript_direct:" + id;
//noinspection unchecked
functionRegistry.forEach((key, function) -> parser.registerFunction(key.getID(), function)); // Register registry functions.
parser
.registerFunction("block", new BlockFunctionBuilder(platform))
.registerFunction("debugBlock", new BlockFunctionBuilder(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<Number>(arguments -> arguments.getOrigin().getX(),
Returnable.ReturnType.NUMBER))
.registerFunction("originY", new ZeroArgFunctionBuilder<Number>(arguments -> arguments.getOrigin().getY(),
Returnable.ReturnType.NUMBER))
.registerFunction("originZ", new ZeroArgFunctionBuilder<Number>(arguments -> arguments.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 -> LOGGER.info("[TerraScript:{}] {}", id, string)))
.registerFunction("abs", new UnaryNumberFunctionBuilder(number -> FastMath.abs(number.doubleValue())))
.registerFunction("pow2", new UnaryNumberFunctionBuilder(number -> FastMath.pow2(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;
}
@Override
@SuppressWarnings("try")
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
platform.getProfiler().push(profile);
boolean result = applyBlock(new TerraImplementationArguments(location, rotation, random, world, 0));
platform.getProfiler().pop(profile);
return result;
}
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation, int recursions) {
platform.getProfiler().push(profile);
boolean result = applyBlock(new TerraImplementationArguments(location, rotation, random, world, recursions));
platform.getProfiler().pop(profile);
return result;
}
private boolean applyBlock(TerraImplementationArguments arguments) {
try {
return block.execute(arguments);
} catch(RuntimeException e) {
LOGGER.error("Failed to generate structure at {}", arguments.getOrigin(), e);
return false;
}
}
@Override
public RegistryKey getRegistryKey() {
return id;
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments;
import com.dfsek.terra.api.util.Rotation;
import com.dfsek.terra.api.util.vector.Vector3;
import com.dfsek.terra.api.util.vector.Vector3Int;
import com.dfsek.terra.api.world.WritableWorld;
public class TerraImplementationArguments implements ImplementationArguments {
private final Rotation rotation;
private final Random random;
private final WritableWorld world;
private final Map<Vector3, String> marks = new HashMap<>();
private final int recursions;
private final Vector3Int origin;
private boolean waterlog = false;
public TerraImplementationArguments(Vector3Int origin, Rotation rotation, Random random, WritableWorld world, int recursions) {
this.rotation = rotation;
this.random = random;
this.world = world;
this.recursions = recursions;
this.origin = origin;
}
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 WritableWorld getWorld() {
return world;
}
public Vector3Int getOrigin() {
return origin;
}
public void setMark(Vector3 pos, String mark) {
marks.put(pos, mark);
}
public String getMark(Vector3 pos) {
return marks.get(pos);
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
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.Scope;
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.tokenizer.Position;
public class BinaryNumberFunctionBuilder implements FunctionBuilder<Function<Number>> {
private final BiFunction<Number, Number, Number> function;
public BinaryNumberFunctionBuilder(BiFunction<Number, Number, Number> function) {
this.function = function;
}
@Override
public Function<Number> build(List<Returnable<?>> argumentList, Position position) {
return new Function<>() {
@Override
public ReturnType returnType() {
return ReturnType.NUMBER;
}
@SuppressWarnings("unchecked")
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
return function.apply(((Returnable<Number>) argumentList.get(0)).apply(implementationArguments, scope),
((Returnable<Number>) argumentList.get(1)).apply(implementationArguments, scope));
}
@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;
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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<BiomeFunction> {
private final Platform platform;
public BiomeFunctionBuilder(Platform platform) {
this.platform = platform;
}
@SuppressWarnings("unchecked")
@Override
public BiomeFunction build(List<Returnable<?>> argumentList, Position position) {
return new BiomeFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) 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;
};
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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<BlockFunction> {
private final Platform platform;
public BlockFunctionBuilder(Platform platform) {
this.platform = platform;
}
@SuppressWarnings("unchecked")
@Override
public BlockFunction build(List<Returnable<?>> argumentList, Position position) {
if(argumentList.size() < 4) throw new ParseException("Expected data", position);
Returnable<Boolean> overwrite = new BooleanConstant(true, position);
if(argumentList.size() >= 5) overwrite = (Returnable<Boolean>) argumentList.get(4);
Returnable<Boolean> physics = new BooleanConstant(false, position);
if(argumentList.size() == 6) physics = (Returnable<Boolean>) argumentList.get(5);
if(argumentList.get(3) instanceof StringConstant) {
return new BlockFunction.Constant((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (StringConstant) argumentList.get(3),
overwrite, physics, platform, position);
}
return new BlockFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) argumentList.get(3), overwrite, physics,
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, 5 -> Returnable.ReturnType.BOOLEAN;
default -> null;
};
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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<CheckBlockFunction> {
@SuppressWarnings("unchecked")
@Override
public CheckBlockFunction build(List<Returnable<?>> argumentList, Position position) {
return new CheckBlockFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) 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;
};
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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.EntityFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform;
public class EntityFunctionBuilder implements FunctionBuilder<EntityFunction> {
private final Platform platform;
public EntityFunctionBuilder(Platform platform) {
this.platform = platform;
}
@SuppressWarnings("unchecked")
@Override
public EntityFunction build(List<Returnable<?>> argumentList, Position position) {
return new EntityFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) 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;
};
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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<GetMarkFunction> {
public GetMarkFunctionBuilder() {
}
@SuppressWarnings("unchecked")
@Override
public GetMarkFunction build(List<Returnable<?>> argumentList, Position position) {
return new GetMarkFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) 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;
};
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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<LootFunction> {
private final Platform platform;
private final Registry<LootTable> registry;
private final StructureScript script;
public LootFunctionBuilder(Platform platform, Registry<LootTable> registry, StructureScript script) {
this.platform = platform;
this.registry = registry;
this.script = script;
}
@SuppressWarnings("unchecked")
@Override
public LootFunction build(List<Returnable<?>> argumentList, Position position) {
return new LootFunction(registry, (Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) 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;
};
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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.PullFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform;
public class PullFunctionBuilder implements FunctionBuilder<PullFunction> {
private final Platform platform;
public PullFunctionBuilder(Platform platform) {
this.platform = platform;
}
@SuppressWarnings("unchecked")
@Override
public PullFunction build(List<Returnable<?>> argumentList, Position position) {
return new PullFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) 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;
};
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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.RandomFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class RandomFunctionBuilder implements FunctionBuilder<RandomFunction> {
@SuppressWarnings("unchecked")
@Override
public RandomFunction build(List<Returnable<?>> argumentList, Position position) {
return new RandomFunction((Returnable<Number>) 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;
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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.RecursionsFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class RecursionsFunctionBuilder implements FunctionBuilder<RecursionsFunction> {
@Override
public RecursionsFunction build(List<Returnable<?>> argumentList, Position position) {
return new RecursionsFunction(position);
}
@Override
public int argNumber() {
return 0;
}
@Override
public Returnable.ReturnType getArgument(int position) {
return null;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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.SetMarkFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class SetMarkFunctionBuilder implements FunctionBuilder<SetMarkFunction> {
public SetMarkFunctionBuilder() {
}
@SuppressWarnings("unchecked")
@Override
public SetMarkFunction build(List<Returnable<?>> argumentList, Position position) {
return new SetMarkFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) 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;
};
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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<StateFunction> {
private final Platform platform;
public StateFunctionBuilder(Platform platform) {
this.platform = platform;
}
@SuppressWarnings("unchecked")
@Override
public StateFunction build(List<Returnable<?>> argumentList, Position position) {
if(argumentList.size() < 4) throw new ParseException("Expected data", position);
return new StateFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) 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;
};
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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<StructureFunction> {
private final Registry<Structure> registry;
private final Platform platform;
public StructureFunctionBuilder(Registry<Structure> registry, Platform platform) {
this.registry = registry;
this.platform = platform;
}
@SuppressWarnings("unchecked")
@Override
public StructureFunction build(List<Returnable<?>> argumentList, Position position) {
if(argumentList.size() < 5) throw new ParseException("Expected rotations", position);
return new StructureFunction((Returnable<Number>) argumentList.remove(0), (Returnable<Number>) argumentList.remove(0),
(Returnable<Number>) argumentList.remove(0), (Returnable<String>) argumentList.remove(0),
argumentList.stream().map(item -> ((Returnable<String>) 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;
};
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
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.Scope;
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.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class UnaryBooleanFunctionBuilder implements FunctionBuilder<Function<Void>> {
private final BiConsumer<Boolean, TerraImplementationArguments> function;
public UnaryBooleanFunctionBuilder(BiConsumer<Boolean, TerraImplementationArguments> function) {
this.function = function;
}
@Override
public Function<Void> build(List<Returnable<?>> argumentList, Position position) {
return new Function<>() {
@Override
public ReturnType returnType() {
return ReturnType.VOID;
}
@SuppressWarnings("unchecked")
@Override
public Void apply(ImplementationArguments implementationArguments, Scope scope) {
function.accept(((Returnable<Boolean>) argumentList.get(0)).apply(implementationArguments, scope),
(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;
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
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.Scope;
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.tokenizer.Position;
public class UnaryNumberFunctionBuilder implements FunctionBuilder<Function<Number>> {
private final java.util.function.Function<Number, Number> function;
public UnaryNumberFunctionBuilder(java.util.function.Function<Number, Number> function) {
this.function = function;
}
@Override
public Function<Number> build(List<Returnable<?>> argumentList, Position position) {
return new Function<>() {
@Override
public ReturnType returnType() {
return ReturnType.NUMBER;
}
@SuppressWarnings("unchecked")
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
return function.apply(((Returnable<Number>) argumentList.get(0)).apply(implementationArguments, scope));
}
@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;
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
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.Scope;
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.tokenizer.Position;
public class UnaryStringFunctionBuilder implements FunctionBuilder<Function<Void>> {
private final java.util.function.Consumer<String> function;
public UnaryStringFunctionBuilder(java.util.function.Consumer<String> function) {
this.function = function;
}
@Override
public Function<Void> build(List<Returnable<?>> argumentList, Position position) {
return new Function<>() {
@Override
public ReturnType returnType() {
return ReturnType.VOID;
}
@SuppressWarnings("unchecked")
@Override
public Void apply(ImplementationArguments implementationArguments, Scope scope) {
function.accept(((Returnable<String>) argumentList.get(0)).apply(implementationArguments, scope));
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;
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
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.Scope;
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.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ZeroArgFunctionBuilder<T> implements FunctionBuilder<Function<T>> {
private final java.util.function.Function<TerraImplementationArguments, T> function;
private final Returnable.ReturnType type;
public ZeroArgFunctionBuilder(java.util.function.Function<TerraImplementationArguments, T> function, Returnable.ReturnType type) {
this.function = function;
this.type = type;
}
@Override
public Function<T> build(List<Returnable<?>> argumentList, Position position) {
return new Function<>() {
@Override
public ReturnType returnType() {
return type;
}
@Override
public T apply(ImplementationArguments implementationArguments, Scope scope) {
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;
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script.functions;
import net.jafama.FastMath;
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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
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<String> {
private final Returnable<Number> x, y, z;
private final Position position;
public BiomeFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Position position) {
this.x = x;
this.y = y;
this.z = z;
this.position = position;
}
@Override
public String apply(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()),
arguments.getRotation());
BiomeProvider grid = arguments.getWorld().getBiomeProvider();
return grid.getBiome(arguments.getOrigin()
.toVector3()
.mutable()
.add(Vector3.of(FastMath.roundToInt(xz.getX()),
y.apply(implementationArguments, scope).intValue(),
FastMath.roundToInt(xz.getZ()))).immutable(), arguments.getWorld().getSeed()).getID();
}
@Override
public Position getPosition() {
return position;
}
@Override
public ReturnType returnType() {
return ReturnType.STRING;
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script.functions;
import net.jafama.FastMath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
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.Scope;
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.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 BlockFunction implements Function<Void> {
private static final Logger logger = LoggerFactory.getLogger(BlockFunction.class);
protected final Returnable<Number> x, y, z;
protected final Returnable<String> blockData;
protected final Platform platform;
private final Map<String, BlockState> data = new HashMap<>();
private final Returnable<Boolean> overwrite;
private final Returnable<Boolean> physics;
private final Position position;
public BlockFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Returnable<String> blockData,
Returnable<Boolean> overwrite, Returnable<Boolean> physics, 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;
this.physics = physics;
}
@Override
public Void apply(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
BlockState rot = getBlockState(implementationArguments, scope);
setBlock(implementationArguments, scope, arguments, rot);
return null;
}
@Override
public Position getPosition() {
return position;
}
@Override
public ReturnType returnType() {
return ReturnType.VOID;
}
void setBlock(ImplementationArguments implementationArguments, Scope scope,
TerraImplementationArguments arguments, BlockState rot) {
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()), arguments.getRotation());
try {
Vector3.Mutable set = Vector3.of(FastMath.roundToInt(xz.getX()),
y.apply(implementationArguments, scope).doubleValue(),
FastMath.roundToInt(xz.getZ())).mutable().add(arguments.getOrigin());
BlockState current = arguments.getWorld().getBlockState(set);
if(overwrite.apply(implementationArguments, scope) || current.isAir()) {
arguments.getWorld().setBlockState(set, rot, physics.apply(implementationArguments, scope));
}
} catch(RuntimeException e) {
logger.error("Failed to place block at location {}", arguments.getOrigin(), e);
}
}
protected BlockState getBlockState(ImplementationArguments arguments, Scope scope) {
return data.computeIfAbsent(blockData.apply(arguments, scope), platform.getWorldHandle()::createBlockState);
}
public static class Constant extends BlockFunction {
private final BlockState state;
public Constant(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, StringConstant blockData,
Returnable<Boolean> overwrite, Returnable<Boolean> physics, Platform platform, Position position) {
super(x, y, z, blockData, overwrite, physics, platform, position);
this.state = platform.getWorldHandle().createBlockState(blockData.getConstant());
}
@Override
protected BlockState getBlockState(ImplementationArguments arguments, Scope scope) {
return state;
}
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script.functions;
import net.jafama.FastMath;
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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
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<String> {
private final Returnable<Number> x, y, z;
private final Position position;
public CheckBlockFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Position position) {
this.x = x;
this.y = y;
this.z = z;
this.position = position;
}
@Override
public String apply(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()),
arguments.getRotation());
String data = arguments.getWorld()
.getBlockState(arguments.getOrigin()
.toVector3()
.mutable()
.add(Vector3.of(FastMath.roundToInt(xz.getX()),
y.apply(implementationArguments, scope)
.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;
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script.functions;
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.Scope;
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.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
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.util.RotationUtil;
import com.dfsek.terra.api.util.vector.Vector2;
import com.dfsek.terra.api.util.vector.Vector3;
public class EntityFunction implements Function<Void> {
private final EntityType data;
private final Returnable<Number> x, y, z;
private final Position position;
private final Platform platform;
public EntityFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Returnable<String> 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<String>) data).getConstant());
this.x = x;
this.y = y;
this.z = z;
}
@Override
public Void apply(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()), arguments.getRotation());
Entity entity = arguments.getWorld().spawnEntity(Vector3.of(xz.getX(), y.apply(implementationArguments, scope).doubleValue(),
xz.getZ())
.mutable()
.add(arguments.getOrigin())
.add(0.5, 0, 0.5)
.immutable(), data);
platform.getEventManager().callEvent(new EntitySpawnEvent(entity.world().getPack(), entity));
return null;
}
@Override
public Position getPosition() {
return position;
}
@Override
public ReturnType returnType() {
return ReturnType.VOID;
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script.functions;
import net.jafama.FastMath;
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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
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<String> {
private final Returnable<Number> x, y, z;
private final Position position;
public GetMarkFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Position position) {
this.position = position;
this.x = x;
this.y = y;
this.z = z;
}
@Override
public String apply(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()), arguments.getRotation());
String mark = arguments.getMark(Vector3.of(FastMath.floorToInt(xz.getX()), FastMath.floorToInt(
y.apply(implementationArguments, scope).doubleValue()),
FastMath.floorToInt(xz.getZ()))
.mutable()
.add(arguments.getOrigin())
.immutable());
return mark == null ? "" : mark;
}
@Override
public Position getPosition() {
return position;
}
@Override
public ReturnType returnType() {
return ReturnType.STRING;
}
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script.functions;
import net.jafama.FastMath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Random;
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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
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.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.registry.Registry;
import com.dfsek.terra.api.registry.key.RegistryKey;
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<Void> {
private static final Logger LOGGER = LoggerFactory.getLogger(LootFunction.class);
private final Registry<LootTable> registry;
private final Returnable<String> data;
private final Returnable<Number> x, y, z;
private final Position position;
private final Platform platform;
private final StructureScript script;
public LootFunction(Registry<LootTable> registry, Returnable<Number> x, Returnable<Number> y, Returnable<Number> z,
Returnable<String> 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, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()),
arguments.getRotation());
String id = data.apply(implementationArguments, scope);
registry.get(RegistryKey.parse(id))
.ifPresentOrElse(table -> {
Vector3 apply = Vector3.of(FastMath.roundToInt(xz.getX()),
y.apply(implementationArguments, scope)
.intValue(),
FastMath.roundToInt(xz.getZ())).mutable().add(arguments.getOrigin()).immutable();
try {
BlockEntity data = arguments.getWorld().getBlockEntity(apply);
if(!(data instanceof Container container)) {
LOGGER.error("Failed to place loot at {}; block {} is not a container",
apply, data);
return;
}
LootPopulateEvent event = new LootPopulateEvent(container, table,
arguments.getWorld().getPack(), script);
platform.getEventManager().callEvent(event);
if(event.isCancelled()) return;
event.getTable().fillInventory(container.getInventory(),
new Random(apply.hashCode()));
data.update(false);
} catch(Exception e) {
LOGGER.error("Could not apply loot at {}", apply, e);
e.printStackTrace();
}
},
() -> LOGGER.error("No such loot table {}", id));
return null;
}
@Override
public Position getPosition() {
return position;
}
@Override
public ReturnType returnType() {
return ReturnType.VOID;
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script.functions;
import net.jafama.FastMath;
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.Scope;
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.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<Void> {
private final BlockState data;
private final Returnable<Number> x, y, z;
private final Position position;
public PullFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Returnable<String> 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().createBlockState(((ConstantExpression<String>) data).getConstant());
this.x = x;
this.y = y;
this.z = z;
}
@Override
public Void apply(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()), arguments.getRotation());
Vector3.Mutable mutable = Vector3.of(FastMath.roundToInt(xz.getX()), y.apply(implementationArguments, scope).intValue(),
FastMath.roundToInt(xz.getZ())).mutable().add(arguments.getOrigin());
while(mutable.getY() > arguments.getWorld().getMinHeight()) {
if(!arguments.getWorld().getBlockState(mutable).isAir()) {
arguments.getWorld().setBlockState(mutable, data);
break;
}
mutable.subtract(0, 1, 0);
}
return null;
}
@Override
public Position getPosition() {
return position;
}
@Override
public ReturnType returnType() {
return ReturnType.VOID;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script.functions;
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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class RandomFunction implements Function<Integer> {
private final Returnable<Number> numberReturnable;
private final Position position;
public RandomFunction(Returnable<Number> numberReturnable, Position position) {
this.numberReturnable = numberReturnable;
this.position = position;
}
@Override
public ReturnType returnType() {
return ReturnType.NUMBER;
}
@Override
public Integer apply(ImplementationArguments implementationArguments, Scope scope) {
return ((TerraImplementationArguments) implementationArguments).getRandom().nextInt(
numberReturnable.apply(implementationArguments, scope).intValue());
}
@Override
public Position getPosition() {
return position;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script.functions;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class RecursionsFunction implements Function<Number> {
private final Position position;
public RecursionsFunction(Position position) {
this.position = position;
}
@Override
public ReturnType returnType() {
return ReturnType.NUMBER;
}
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
return ((TerraImplementationArguments) implementationArguments).getRecursions();
}
@Override
public Position getPosition() {
return position;
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script.functions;
import net.jafama.FastMath;
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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
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<Void> {
private final Returnable<Number> x, y, z;
private final Position position;
private final Returnable<String> mark;
public SetMarkFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Returnable<String> mark, Position position) {
this.position = position;
this.mark = mark;
this.x = x;
this.y = y;
this.z = z;
}
@Override
public Void apply(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()), arguments.getRotation());
arguments.setMark(Vector3.of(FastMath.floorToInt(xz.getX()),
FastMath.floorToInt(
y.apply(implementationArguments, scope).doubleValue()),
FastMath.floorToInt(xz.getZ())).mutable().add(arguments.getOrigin()).immutable(),
mark.apply(implementationArguments, scope));
return null;
}
@Override
public Position getPosition() {
return position;
}
@Override
public ReturnType returnType() {
return ReturnType.VOID;
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script.functions;
import net.jafama.FastMath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.block.entity.BlockEntity;
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<Void> {
private static final Logger LOGGER = LoggerFactory.getLogger(StateFunction.class);
private final Returnable<String> data;
private final Returnable<Number> x, y, z;
private final Position position;
public StateFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Returnable<String> data,
Position position) {
this.position = position;
this.data = data;
this.x = x;
this.y = y;
this.z = z;
}
@Override
public Void apply(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()), arguments.getRotation());
Vector3 origin = Vector3.of(FastMath.roundToInt(xz.getX()), y.apply(implementationArguments, scope).intValue(),
FastMath.roundToInt(xz.getZ())).mutable().add(arguments.getOrigin()).immutable();
try {
BlockEntity state = arguments.getWorld().getBlockEntity(origin);
state.applyState(data.apply(implementationArguments, scope));
state.update(false);
} catch(Exception e) {
LOGGER.warn("Could not apply BlockState at {}", origin, e);
e.printStackTrace();
}
return null;
}
@Override
public Position getPosition() {
return position;
}
@Override
public ReturnType returnType() {
return ReturnType.VOID;
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.script.functions;
import net.jafama.FastMath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
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.Structure;
import com.dfsek.terra.api.util.Rotation;
import com.dfsek.terra.api.util.RotationUtil;
import com.dfsek.terra.api.util.vector.Vector2;
public class StructureFunction implements Function<Boolean> {
private static final Logger LOGGER = LoggerFactory.getLogger(StructureFunction.class);
private final Registry<Structure> registry;
private final Returnable<String> id;
private final Returnable<Number> x, y, z;
private final Position position;
private final Platform platform;
private final List<Returnable<String>> rotations;
public StructureFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Returnable<String> id,
List<Returnable<String>> rotations, Registry<Structure> 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, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
if(arguments.getRecursions() > platform.getTerraConfig().getMaxRecursion())
throw new RuntimeException("Structure recursion too deep: " + arguments.getRecursions());
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()), arguments.getRotation());
String app = id.apply(implementationArguments, scope);
return registry.getByID(app).map(script -> {
Rotation rotation1;
String rotString = rotations.get(arguments.getRandom().nextInt(rotations.size())).apply(implementationArguments, scope);
try {
rotation1 = Rotation.valueOf(rotString);
} catch(IllegalArgumentException e) {
LOGGER.warn("Invalid rotation {}", rotString);
return false;
}
if(script instanceof StructureScript structureScript) {
return structureScript.generate(arguments.getOrigin(),
arguments.getWorld()
.buffer(FastMath.roundToInt(xz.getX()),
y.apply(implementationArguments, scope).intValue(),
FastMath.roundToInt(xz.getZ())),
arguments.getRandom(),
arguments.getRotation().rotate(rotation1), arguments.getRecursions() + 1);
}
return script.generate(arguments.getOrigin(),
arguments.getWorld()
.buffer(FastMath.roundToInt(xz.getX()),
y.apply(implementationArguments, scope).intValue(),
FastMath.roundToInt(xz.getZ())),
arguments.getRandom(),
arguments.getRotation().rotate(rotation1));
}).orElseGet(() -> {
LOGGER.error("No such structure {}", app);
return false;
});
}
@Override
public Position getPosition() {
return position;
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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';
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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<Char> 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;
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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;
}
}

View File

@@ -0,0 +1,237 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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
}
}

View File

@@ -0,0 +1,247 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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<Character> syntaxSignificant = Sets.newHashSet(';', '(', ')', '"', ',', '\\', '=', '{', '}', '+', '-', '*', '/',
'>', '<', '!'); // Reserved chars
private final Lookahead reader;
private final Stack<Token> 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);
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.tokenizer.exceptions;
import java.io.Serial;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class EOFException extends TokenizerException {
@Serial
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);
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.tokenizer.exceptions;
import java.io.Serial;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class FormatException extends TokenizerException {
@Serial
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);
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
package com.dfsek.terra.addons.terrascript.tokenizer.exceptions;
import java.io.Serial;
import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public abstract class TokenizerException extends ParseException {
@Serial
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);
}
}

View File

@@ -0,0 +1,12 @@
schema-version: 1
contributors:
- Terra contributors
id: structure-terrascript-loader
version: @VERSION@
entrypoints:
- "com.dfsek.terra.addons.terrascript.TerraScriptAddon"
website:
issues: https://github.com/PolyhedralDev/Terra/issues
source: https://github.com/PolyhedralDev/Terra
docs: https://terra.polydev.org
license: MIT License

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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());
}
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (c) 2020-2021 Polyhedral Development
*
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in this module's root directory.
*/
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.List;
import java.util.Objects;
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.Executable;
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.Scope;
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.tokenizer.Position;
public class ParserTest {
@Test
public void parse() throws IOException, ParseException {
Parser parser = new Parser(
IOUtils.toString(Objects.requireNonNull(getClass().getResourceAsStream("/test.tesf")), Charset.defaultCharset()));
parser.registerFunction("test", new FunctionBuilder<Test1>() {
@Override
public Test1 build(List<Returnable<?>> 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();
Executable block = parser.parse();
long t = System.nanoTime() - l;
System.out.println("Took " + (double) t / 1000000);
block.execute(null);
block.execute(null);
}
private static class Test1 implements Function<Void> {
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, Scope scope) {
System.out.println("string: " + a.apply(implementationArguments, scope) + ", double: " +
b.apply(implementationArguments, scope));
return null;
}
@Override
public Position getPosition() {
return position;
}
@Override
public ReturnType returnType() {
return ReturnType.VOID;
}
}
}

Some files were not shown because too many files have changed in this diff Show More