From 5d6b060deebad0d932f45a8f9f23854640c4c676 Mon Sep 17 00:00:00 2001 From: dfsek Date: Thu, 24 Dec 2020 02:06:05 -0700 Subject: [PATCH] Add math functions --- .../terra/api/structures/parser/Parser.java | 58 ++++++++++++++----- .../lang/functions/builtin/AbsFunction.java | 32 ++++++++++ .../lang/functions/builtin/MathFunction.java | 22 +++++++ .../lang/functions/builtin/PowFunction.java | 34 +++++++++++ .../lang/functions/builtin/SqrtFunction.java | 32 ++++++++++ 5 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 common/src/main/java/com/dfsek/terra/api/structures/parser/lang/functions/builtin/AbsFunction.java create mode 100644 common/src/main/java/com/dfsek/terra/api/structures/parser/lang/functions/builtin/MathFunction.java create mode 100644 common/src/main/java/com/dfsek/terra/api/structures/parser/lang/functions/builtin/PowFunction.java create mode 100644 common/src/main/java/com/dfsek/terra/api/structures/parser/lang/functions/builtin/SqrtFunction.java diff --git a/common/src/main/java/com/dfsek/terra/api/structures/parser/Parser.java b/common/src/main/java/com/dfsek/terra/api/structures/parser/Parser.java index 0826ff12c..0e17da62f 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/parser/Parser.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/parser/Parser.java @@ -11,6 +11,9 @@ import com.dfsek.terra.api.structures.parser.lang.constants.NumericConstant; import com.dfsek.terra.api.structures.parser.lang.constants.StringConstant; import com.dfsek.terra.api.structures.parser.lang.functions.Function; import com.dfsek.terra.api.structures.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.api.structures.parser.lang.functions.builtin.AbsFunction; +import com.dfsek.terra.api.structures.parser.lang.functions.builtin.PowFunction; +import com.dfsek.terra.api.structures.parser.lang.functions.builtin.SqrtFunction; import com.dfsek.terra.api.structures.parser.lang.keywords.BreakKeyword; import com.dfsek.terra.api.structures.parser.lang.keywords.ContinueKeyword; import com.dfsek.terra.api.structures.parser.lang.keywords.IfKeyword; @@ -42,14 +45,18 @@ import com.dfsek.terra.api.structures.tokenizer.Token; import com.dfsek.terra.api.structures.tokenizer.Tokenizer; import com.dfsek.terra.api.structures.tokenizer.exceptions.TokenizerException; import com.dfsek.terra.api.util.GlueList; +import com.google.common.collect.Sets; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; public class Parser { private final String data; private final Map>> functions = new HashMap<>(); + private final Set builtinFunctions = Sets.newHashSet("abs", "sqrt", "pow"); + private String id; public Parser(String data) { @@ -141,7 +148,8 @@ public class Parser { } else if(id.getType().equals(Token.Type.GROUP_BEGIN)) { // Parse grouped expression expression = parseGroup(tokens, variableMap); } else { - if(functions.containsKey(id.getContent())) expression = parseFunction(tokens, false, variableMap); + if(functions.containsKey(id.getContent()) || builtinFunctions.contains(id.getContent())) + expression = parseFunction(tokens, false, variableMap); else if(variableMap.containsKey(id.getContent())) { ParserUtil.checkType(tokens.remove(0), Token.Type.IDENTIFIER); expression = new Getter(variableMap.get(id.getContent())); @@ -280,7 +288,7 @@ public class Parser { ParserUtil.checkType(name, Token.Type.IDENTIFIER); // Name must be an identifier. - if(functions.containsKey(name.getContent()) || parsedVariables.containsKey(name.getContent())) + if(functions.containsKey(name.getContent()) || parsedVariables.containsKey(name.getContent()) || builtinFunctions.contains(name.getContent())) throw new ParseException(name.getContent() + " is already defined in this scope: " + name.getPosition()); parsedVariables.put(name.getContent(), temp); @@ -313,11 +321,12 @@ public class Parser { return new Assignment<>((Variable) variable, (Returnable) expression, name.getPosition()); } + @SuppressWarnings("unchecked") private Function parseFunction(List tokens, boolean fullStatement, Map> variableMap) throws ParseException { Token identifier = tokens.remove(0); ParserUtil.checkType(identifier, Token.Type.IDENTIFIER); // First token must be identifier - if(!functions.containsKey(identifier.getContent())) + if(!functions.containsKey(identifier.getContent()) && !builtinFunctions.contains(identifier.getContent())) throw new ParseException("No such function " + identifier.getContent() + ": " + identifier.getPosition()); ParserUtil.checkType(tokens.remove(0), Token.Type.GROUP_BEGIN); // Second is body begin @@ -325,22 +334,45 @@ public class Parser { List> args = getArgs(tokens, variableMap); // Extract arguments, consume the rest. - tokens.remove(0); // Remove body end + ParserUtil.checkType(tokens.remove(0), Token.Type.GROUP_END); // Remove body end if(fullStatement) ParserUtil.checkType(tokens.get(0), Token.Type.STATEMENT_END); - FunctionBuilder builder = functions.get(identifier.getContent()); + 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()); + 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)); + 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()); + } else { + switch(identifier.getContent()) { + case "abs": + ParserUtil.checkReturnType(args.get(0), Returnable.ReturnType.NUMBER); + if(args.size() != 1) + throw new ParseException("Expected 1 arguments; found " + args.size() + ": " + identifier.getPosition()); + return new AbsFunction(identifier.getPosition(), (Returnable) args.get(0)); + case "sqrt": + ParserUtil.checkReturnType(args.get(0), Returnable.ReturnType.NUMBER); + if(args.size() != 1) + throw new ParseException("Expected 1 arguments; found " + args.size() + ": " + identifier.getPosition()); + return new SqrtFunction(identifier.getPosition(), (Returnable) args.get(0)); + case "pow": + ParserUtil.checkReturnType(args.get(0), Returnable.ReturnType.NUMBER); + ParserUtil.checkReturnType(args.get(1), Returnable.ReturnType.NUMBER); + if(args.size() != 2) + throw new ParseException("Expected 1 arguments; found " + args.size() + ": " + identifier.getPosition()); + return new PowFunction(identifier.getPosition(), (Returnable) args.get(0), (Returnable) args.get(1)); + default: + throw new UnsupportedOperationException("Unsupported function: " + identifier.getContent()); + } } - return builder.build(args, identifier.getPosition()); } diff --git a/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/functions/builtin/AbsFunction.java b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/functions/builtin/AbsFunction.java new file mode 100644 index 000000000..fa70f33f2 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/functions/builtin/AbsFunction.java @@ -0,0 +1,32 @@ +package com.dfsek.terra.api.structures.parser.lang.functions.builtin; + +import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.world.Chunk; +import com.dfsek.terra.api.structures.parser.lang.Returnable; +import com.dfsek.terra.api.structures.structure.Rotation; +import com.dfsek.terra.api.structures.tokenizer.Position; +import net.jafama.FastMath; + +public class AbsFunction extends MathFunction { + private final Returnable returnable; + + public AbsFunction(Position position, Returnable returnable) { + super(position); + this.returnable = returnable; + } + + @Override + public String name() { + return "abs"; + } + + @Override + public Number apply(Location location, Rotation rotation, int recursions) { + return FastMath.abs(returnable.apply(location, rotation, recursions).doubleValue()); + } + + @Override + public Number apply(Location location, Chunk chunk, Rotation rotation, int recursions) { + return FastMath.abs(returnable.apply(location, chunk, rotation, recursions).doubleValue()); + } +} diff --git a/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/functions/builtin/MathFunction.java b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/functions/builtin/MathFunction.java new file mode 100644 index 000000000..f05da3b83 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/functions/builtin/MathFunction.java @@ -0,0 +1,22 @@ +package com.dfsek.terra.api.structures.parser.lang.functions.builtin; + +import com.dfsek.terra.api.structures.parser.lang.functions.Function; +import com.dfsek.terra.api.structures.tokenizer.Position; + +public abstract class MathFunction implements Function { + private final Position position; + + protected MathFunction(Position position) { + this.position = position; + } + + @Override + public ReturnType returnType() { + return ReturnType.NUMBER; + } + + @Override + public Position getPosition() { + return position; + } +} diff --git a/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/functions/builtin/PowFunction.java b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/functions/builtin/PowFunction.java new file mode 100644 index 000000000..0b09d3c48 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/functions/builtin/PowFunction.java @@ -0,0 +1,34 @@ +package com.dfsek.terra.api.structures.parser.lang.functions.builtin; + +import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.world.Chunk; +import com.dfsek.terra.api.structures.parser.lang.Returnable; +import com.dfsek.terra.api.structures.structure.Rotation; +import com.dfsek.terra.api.structures.tokenizer.Position; +import net.jafama.FastMath; + +public class PowFunction extends MathFunction { + private final Returnable base; + private final Returnable power; + + public PowFunction(Position position, Returnable base, Returnable power) { + super(position); + this.base = base; + this.power = power; + } + + @Override + public String name() { + return "pow"; + } + + @Override + public Number apply(Location location, Rotation rotation, int recursions) { + return FastMath.pow(base.apply(location, rotation, recursions).doubleValue(), power.apply(location, rotation, recursions).doubleValue()); + } + + @Override + public Number apply(Location location, Chunk chunk, Rotation rotation, int recursions) { + return FastMath.pow(base.apply(location, chunk, rotation, recursions).doubleValue(), power.apply(location, chunk, rotation, recursions).doubleValue()); + } +} diff --git a/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/functions/builtin/SqrtFunction.java b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/functions/builtin/SqrtFunction.java new file mode 100644 index 000000000..62ade97be --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/structures/parser/lang/functions/builtin/SqrtFunction.java @@ -0,0 +1,32 @@ +package com.dfsek.terra.api.structures.parser.lang.functions.builtin; + +import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.world.Chunk; +import com.dfsek.terra.api.structures.parser.lang.Returnable; +import com.dfsek.terra.api.structures.structure.Rotation; +import com.dfsek.terra.api.structures.tokenizer.Position; +import net.jafama.FastMath; + +public class SqrtFunction extends MathFunction { + private final Returnable returnable; + + public SqrtFunction(Position position, Returnable returnable) { + super(position); + this.returnable = returnable; + } + + @Override + public String name() { + return "sqrt"; + } + + @Override + public Number apply(Location location, Rotation rotation, int recursions) { + return FastMath.sqrt(returnable.apply(location, rotation, recursions).doubleValue()); + } + + @Override + public Number apply(Location location, Chunk chunk, Rotation rotation, int recursions) { + return FastMath.sqrt(returnable.apply(location, chunk, rotation, recursions).doubleValue()); + } +}