Compare commits

...

25 Commits

Author SHA1 Message Date
Astrash 772675639e Better error handling + other changes 2023-07-29 20:29:16 +10:00
Astrash 13300861ee Parse precedence via grammar 2023-07-29 12:03:33 +10:00
Astrash 719b9a06f4 Simplify code 2023-07-29 08:57:43 +10:00
Astrash f5b115e618 Formatting & name changes 2023-07-27 12:52:15 +10:00
Astrash e1e4a63517 Add basic user defined function support 2023-07-27 11:27:15 +10:00
Astrash 0dc1492c4d Handle functions in scope 2023-07-25 14:08:09 +10:00
Astrash a184fe40d0 Name changes 2023-07-24 18:52:54 +10:00
Astrash f462b8198b Move inLoop flag to ScopeBuilder 2023-07-24 18:05:43 +10:00
Astrash de3b213deb Refactor some parsing logic 2023-07-24 17:31:06 +10:00
Astrash be444f75b7 Block -> executable 2023-07-24 17:30:37 +10:00
Astrash d98238c262 Remove statement class 2023-07-23 19:33:17 +10:00
Astrash 8e96745a85 checkReturnType -> ensureReturnType 2023-07-23 19:17:50 +10:00
Astrash 802bce40c8 Move statement end handling to parseExpression 2023-07-23 19:17:08 +10:00
Astrash 76728fe593 More refactoring 2023-07-23 17:55:29 +10:00
Astrash f3d1751c87 Terrascript refactor 2023-07-23 16:11:56 +10:00
Astrash 81e354f91c Use tectonic properly 2023-07-18 22:06:08 +10:00
Astrash aab28ff4f9 Bump version to 6.4.0 2023-07-18 14:32:12 +10:00
Astrash 0e3a756011 Bump config-noise-function to v1.1.0 2023-07-18 14:29:54 +10:00
Astrash 02198e1b88 Implement distance sampler 2023-07-18 14:29:28 +10:00
Astrash 00aeb98419 Implement translation sampler 2023-07-18 14:27:36 +10:00
Astrash 1a784b51ac Implement expression normalizer sampler 2023-07-18 14:25:07 +10:00
Astrash 34c0895c1f Make metalist injection error more user friendly 2023-07-16 22:46:23 +10:00
Astrash 379fa601a3 Meta annotate LINEAR_HEIGHTMAP sampler 2023-07-16 17:04:51 +10:00
Astrash fcbf51d80b Allow Range keys to be meta annotated 2023-07-16 11:51:51 +10:00
Astrash 9d83dfd164 Bump version to 6.3.2 2023-07-16 11:49:14 +10:00
121 changed files with 2251 additions and 1760 deletions
+3 -3
View File
@@ -1,8 +1,8 @@
preRelease(true) preRelease(true)
versionProjects(":common:api", version("6.3.1")) versionProjects(":common:api", version("6.4.0"))
versionProjects(":common:implementation", version("6.3.1")) versionProjects(":common:implementation", version("6.4.0"))
versionProjects(":platforms", version("6.3.1")) versionProjects(":platforms", version("6.4.0"))
allprojects { allprojects {
@@ -1,6 +1,6 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
version = version("1.0.0") version = version("1.1.0")
dependencies { dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader")) compileOnlyApi(project(":common:addons:manifest-addon-loader"))
@@ -21,8 +21,10 @@ import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
import com.dfsek.terra.addons.noise.config.templates.ImageSamplerTemplate; import com.dfsek.terra.addons.noise.config.templates.ImageSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.KernelTemplate; import com.dfsek.terra.addons.noise.config.templates.KernelTemplate;
import com.dfsek.terra.addons.noise.config.templates.LinearHeightmapSamplerTemplate; import com.dfsek.terra.addons.noise.config.templates.LinearHeightmapSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.TranslateSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.CellularNoiseTemplate; import com.dfsek.terra.addons.noise.config.templates.noise.CellularNoiseTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.ConstantNoiseTemplate; import com.dfsek.terra.addons.noise.config.templates.noise.ConstantNoiseTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.DistanceSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.ExpressionFunctionTemplate; import com.dfsek.terra.addons.noise.config.templates.noise.ExpressionFunctionTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.GaborNoiseTemplate; import com.dfsek.terra.addons.noise.config.templates.noise.GaborNoiseTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.SimpleNoiseTemplate; import com.dfsek.terra.addons.noise.config.templates.noise.SimpleNoiseTemplate;
@@ -30,6 +32,7 @@ import com.dfsek.terra.addons.noise.config.templates.noise.fractal.BrownianMotio
import com.dfsek.terra.addons.noise.config.templates.noise.fractal.PingPongTemplate; import com.dfsek.terra.addons.noise.config.templates.noise.fractal.PingPongTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.fractal.RidgedFractalTemplate; import com.dfsek.terra.addons.noise.config.templates.noise.fractal.RidgedFractalTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.ClampNormalizerTemplate; import com.dfsek.terra.addons.noise.config.templates.normalizer.ClampNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.ExpressionNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.LinearNormalizerTemplate; import com.dfsek.terra.addons.noise.config.templates.normalizer.LinearNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.NormalNormalizerTemplate; import com.dfsek.terra.addons.noise.config.templates.normalizer.NormalNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.PosterizationNormalizerTemplate; import com.dfsek.terra.addons.noise.config.templates.normalizer.PosterizationNormalizerTemplate;
@@ -42,6 +45,7 @@ import com.dfsek.terra.addons.noise.samplers.arithmetic.MinSampler;
import com.dfsek.terra.addons.noise.samplers.arithmetic.MultiplicationSampler; import com.dfsek.terra.addons.noise.samplers.arithmetic.MultiplicationSampler;
import com.dfsek.terra.addons.noise.samplers.arithmetic.SubtractionSampler; import com.dfsek.terra.addons.noise.samplers.arithmetic.SubtractionSampler;
import com.dfsek.terra.addons.noise.samplers.noise.CellularSampler; import com.dfsek.terra.addons.noise.samplers.noise.CellularSampler;
import com.dfsek.terra.addons.noise.samplers.noise.DistanceSampler;
import com.dfsek.terra.addons.noise.samplers.noise.random.GaussianNoiseSampler; import com.dfsek.terra.addons.noise.samplers.noise.random.GaussianNoiseSampler;
import com.dfsek.terra.addons.noise.samplers.noise.random.PositiveWhiteNoiseSampler; import com.dfsek.terra.addons.noise.samplers.noise.random.PositiveWhiteNoiseSampler;
import com.dfsek.terra.addons.noise.samplers.noise.random.WhiteNoiseSampler; import com.dfsek.terra.addons.noise.samplers.noise.random.WhiteNoiseSampler;
@@ -83,6 +87,8 @@ public class NoiseAddon implements AddonInitializer {
(type, o, loader, depthTracker) -> CellularSampler.DistanceFunction.valueOf((String) o)) (type, o, loader, depthTracker) -> CellularSampler.DistanceFunction.valueOf((String) o))
.applyLoader(CellularSampler.ReturnType.class, .applyLoader(CellularSampler.ReturnType.class,
(type, o, loader, depthTracker) -> CellularSampler.ReturnType.valueOf((String) o)) (type, o, loader, depthTracker) -> CellularSampler.ReturnType.valueOf((String) o))
.applyLoader(DistanceSampler.DistanceFunction.class,
(type, o, loader, depthTracker) -> DistanceSampler.DistanceFunction.valueOf((String) o))
.applyLoader(DimensionApplicableNoiseSampler.class, DimensionApplicableNoiseSampler::new) .applyLoader(DimensionApplicableNoiseSampler.class, DimensionApplicableNoiseSampler::new)
.applyLoader(FunctionTemplate.class, FunctionTemplate::new); .applyLoader(FunctionTemplate.class, FunctionTemplate::new);
@@ -116,12 +122,15 @@ public class NoiseAddon implements AddonInitializer {
noiseRegistry.register(addon.key("WHITE_NOISE"), () -> new SimpleNoiseTemplate(WhiteNoiseSampler::new)); noiseRegistry.register(addon.key("WHITE_NOISE"), () -> new SimpleNoiseTemplate(WhiteNoiseSampler::new));
noiseRegistry.register(addon.key("POSITIVE_WHITE_NOISE"), () -> new SimpleNoiseTemplate(PositiveWhiteNoiseSampler::new)); noiseRegistry.register(addon.key("POSITIVE_WHITE_NOISE"), () -> new SimpleNoiseTemplate(PositiveWhiteNoiseSampler::new));
noiseRegistry.register(addon.key("GAUSSIAN"), () -> new SimpleNoiseTemplate(GaussianNoiseSampler::new)); noiseRegistry.register(addon.key("GAUSSIAN"), () -> new SimpleNoiseTemplate(GaussianNoiseSampler::new));
noiseRegistry.register(addon.key("DISTANCE"), DistanceSamplerTemplate::new);
noiseRegistry.register(addon.key("CONSTANT"), ConstantNoiseTemplate::new); noiseRegistry.register(addon.key("CONSTANT"), ConstantNoiseTemplate::new);
noiseRegistry.register(addon.key("KERNEL"), KernelTemplate::new); noiseRegistry.register(addon.key("KERNEL"), KernelTemplate::new);
noiseRegistry.register(addon.key("LINEAR_HEIGHTMAP"), LinearHeightmapSamplerTemplate::new); noiseRegistry.register(addon.key("LINEAR_HEIGHTMAP"), LinearHeightmapSamplerTemplate::new);
noiseRegistry.register(addon.key("TRANSLATE"), TranslateSamplerTemplate::new);
noiseRegistry.register(addon.key("ADD"), () -> new BinaryArithmeticTemplate<>(AdditionSampler::new)); noiseRegistry.register(addon.key("ADD"), () -> new BinaryArithmeticTemplate<>(AdditionSampler::new));
noiseRegistry.register(addon.key("SUB"), () -> new BinaryArithmeticTemplate<>(SubtractionSampler::new)); noiseRegistry.register(addon.key("SUB"), () -> new BinaryArithmeticTemplate<>(SubtractionSampler::new));
@@ -134,7 +143,7 @@ public class NoiseAddon implements AddonInitializer {
Map<String, DimensionApplicableNoiseSampler> packSamplers = new LinkedHashMap<>(); Map<String, DimensionApplicableNoiseSampler> packSamplers = new LinkedHashMap<>();
Map<String, FunctionTemplate> packFunctions = new LinkedHashMap<>(); Map<String, FunctionTemplate> packFunctions = new LinkedHashMap<>();
noiseRegistry.register(addon.key("EXPRESSION"), () -> new ExpressionFunctionTemplate(packSamplers, packFunctions)); noiseRegistry.register(addon.key("EXPRESSION"), () -> new ExpressionFunctionTemplate(packSamplers, packFunctions));
noiseRegistry.register(addon.key("EXPRESSION_NORMALIZER"), () -> new ExpressionNormalizerTemplate(packSamplers, packFunctions));
NoiseConfigPackTemplate template = event.loadTemplate(new NoiseConfigPackTemplate()); NoiseConfigPackTemplate template = event.loadTemplate(new NoiseConfigPackTemplate());
packSamplers.putAll(template.getSamplers()); packSamplers.putAll(template.getSamplers());
@@ -4,6 +4,7 @@ import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value; import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.samplers.LinearHeightmapSampler; import com.dfsek.terra.addons.noise.samplers.LinearHeightmapSampler;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler; import com.dfsek.terra.api.noise.NoiseSampler;
@@ -11,14 +12,14 @@ import com.dfsek.terra.api.noise.NoiseSampler;
public class LinearHeightmapSamplerTemplate extends SamplerTemplate<LinearHeightmapSampler> { public class LinearHeightmapSamplerTemplate extends SamplerTemplate<LinearHeightmapSampler> {
@Value("sampler") @Value("sampler")
@Default @Default
private NoiseSampler sampler = NoiseSampler.zero(); private @Meta NoiseSampler sampler = NoiseSampler.zero();
@Value("base") @Value("base")
private double base; private @Meta double base;
@Value("scale") @Value("scale")
@Default @Default
private double scale = 1; private @Meta double scale = 1;
@Override @Override
public NoiseSampler get() { public NoiseSampler get() {
@@ -0,0 +1,32 @@
package com.dfsek.terra.addons.noise.config.templates;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.samplers.TranslateSampler;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
public class TranslateSamplerTemplate extends SamplerTemplate<TranslateSampler> {
@Value("sampler")
private NoiseSampler sampler;
@Value("x")
@Default
private @Meta double x = 0;
@Value("y")
@Default
private @Meta double y = 0;
@Value("z")
@Default
private @Meta double z = 0;
@Override
public NoiseSampler get() {
return new TranslateSampler(sampler, x, y ,z);
}
}
@@ -0,0 +1,42 @@
package com.dfsek.terra.addons.noise.config.templates.noise;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.config.templates.SamplerTemplate;
import com.dfsek.terra.addons.noise.samplers.noise.DistanceSampler;
import com.dfsek.terra.addons.noise.samplers.noise.DistanceSampler.DistanceFunction;
import com.dfsek.terra.api.config.meta.Meta;
public class DistanceSamplerTemplate extends SamplerTemplate<DistanceSampler> {
@Value("distance-function")
@Default
private DistanceSampler.@Meta DistanceFunction distanceFunction = DistanceFunction.Euclidean;
@Value("point.x")
@Default
private @Meta double x = 0;
@Value("point.y")
@Default
private @Meta double y = 0;
@Value("point.z")
@Default
private @Meta double z = 0;
@Value("normalize")
@Default
private @Meta boolean normalize = false;
@Value("radius")
@Default
private @Meta double normalizeRadius = 100;
@Override
public DistanceSampler get() {
return new DistanceSampler(distanceFunction, x, y, z, normalize, normalizeRadius);
}
}
@@ -8,7 +8,6 @@
package com.dfsek.terra.addons.noise.config.templates.noise; package com.dfsek.terra.addons.noise.config.templates.noise;
import com.dfsek.paralithic.eval.tokenizer.ParseException; import com.dfsek.paralithic.eval.tokenizer.ParseException;
import com.dfsek.paralithic.functions.Function;
import com.dfsek.tectonic.api.config.template.annotations.Default; import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value; import com.dfsek.tectonic.api.config.template.annotations.Value;
@@ -19,17 +18,16 @@ import java.util.Map;
import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler; import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler;
import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate; import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
import com.dfsek.terra.addons.noise.config.templates.SamplerTemplate; import com.dfsek.terra.addons.noise.config.templates.SamplerTemplate;
import com.dfsek.terra.addons.noise.paralithic.defined.UserDefinedFunction;
import com.dfsek.terra.addons.noise.paralithic.noise.NoiseFunction2;
import com.dfsek.terra.addons.noise.paralithic.noise.NoiseFunction3;
import com.dfsek.terra.addons.noise.samplers.noise.ExpressionFunction; import com.dfsek.terra.addons.noise.samplers.noise.ExpressionFunction;
import com.dfsek.terra.api.config.meta.Meta; import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler; import com.dfsek.terra.api.noise.NoiseSampler;
import static com.dfsek.terra.addons.noise.paralithic.FunctionUtil.convertFunctionsAndSamplers;
@SuppressWarnings({ "FieldMayBeFinal", "unused" }) @SuppressWarnings({ "FieldMayBeFinal", "unused" })
public class ExpressionFunctionTemplate extends SamplerTemplate<ExpressionFunction> { public class ExpressionFunctionTemplate extends SamplerTemplate<ExpressionFunction> {
private final Map<String, DimensionApplicableNoiseSampler> otherFunctions; private final Map<String, DimensionApplicableNoiseSampler> globalSamplers;
private final Map<String, FunctionTemplate> globalFunctions; private final Map<String, FunctionTemplate> globalFunctions;
@Value("variables") @Value("variables")
@Default @Default
@@ -43,44 +41,19 @@ public class ExpressionFunctionTemplate extends SamplerTemplate<ExpressionFuncti
@Default @Default
private @Meta LinkedHashMap<String, @Meta FunctionTemplate> functions = new LinkedHashMap<>(); private @Meta LinkedHashMap<String, @Meta FunctionTemplate> functions = new LinkedHashMap<>();
public ExpressionFunctionTemplate(Map<String, DimensionApplicableNoiseSampler> otherFunctions, Map<String, FunctionTemplate> samplers) { public ExpressionFunctionTemplate(Map<String, DimensionApplicableNoiseSampler> globalSamplers, Map<String, FunctionTemplate> globalFunctions) {
this.otherFunctions = otherFunctions; this.globalSamplers = globalSamplers;
this.globalFunctions = samplers; this.globalFunctions = globalFunctions;
} }
@Override @Override
public NoiseSampler get() { public NoiseSampler get() {
var mergedFunctions = new HashMap<>(globalFunctions); mergedFunctions.putAll(functions);
var mergedSamplers = new HashMap<>(globalSamplers); mergedSamplers.putAll(samplers);
try { try {
Map<String, Function> noiseFunctionMap = generateFunctions(); return new ExpressionFunction(convertFunctionsAndSamplers(mergedFunctions, mergedSamplers), expression, vars);
return new ExpressionFunction(noiseFunctionMap, expression, vars);
} catch(ParseException e) { } catch(ParseException e) {
throw new RuntimeException("Failed to parse expression.", e); throw new RuntimeException("Failed to parse expression.", e);
} }
} }
private Map<String, Function> generateFunctions() throws ParseException {
Map<String, Function> noiseFunctionMap = new HashMap<>();
for(Map.Entry<String, FunctionTemplate> entry : globalFunctions.entrySet()) {
noiseFunctionMap.put(entry.getKey(), UserDefinedFunction.newInstance(entry.getValue()));
}
for(Map.Entry<String, FunctionTemplate> entry : functions.entrySet()) {
noiseFunctionMap.put(entry.getKey(), UserDefinedFunction.newInstance(entry.getValue()));
}
otherFunctions.forEach((id, function) -> {
if(function.getDimensions() == 2) {
noiseFunctionMap.put(id, new NoiseFunction2(function.getSampler()));
} else noiseFunctionMap.put(id, new NoiseFunction3(function.getSampler()));
});
samplers.forEach((id, function) -> {
if(function.getDimensions() == 2) {
noiseFunctionMap.put(id, new NoiseFunction2(function.getSampler()));
} else noiseFunctionMap.put(id, new NoiseFunction3(function.getSampler()));
});
return noiseFunctionMap;
}
} }
@@ -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.noise.config.templates.normalizer;
import com.dfsek.paralithic.eval.tokenizer.ParseException;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler;
import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
import com.dfsek.terra.addons.noise.normalizer.ExpressionNormalizer;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import static com.dfsek.terra.addons.noise.paralithic.FunctionUtil.convertFunctionsAndSamplers;
@SuppressWarnings({ "unused", "FieldMayBeFinal" })
public class ExpressionNormalizerTemplate extends NormalizerTemplate<ExpressionNormalizer> {
private final Map<String, DimensionApplicableNoiseSampler> globalSamplers;
private final Map<String, FunctionTemplate> globalFunctions;
@Value("expression")
private @Meta String expression;
@Value("variables")
@Default
private @Meta Map<String, @Meta Double> vars = new HashMap<>();
@Value("samplers")
@Default
private @Meta LinkedHashMap<String, @Meta DimensionApplicableNoiseSampler> samplers = new LinkedHashMap<>();
@Value("functions")
@Default
private @Meta LinkedHashMap<String, @Meta FunctionTemplate> functions = new LinkedHashMap<>();
public ExpressionNormalizerTemplate(Map<String, DimensionApplicableNoiseSampler> globalSamplers, Map<String, FunctionTemplate> globalFunctions) {
this.globalSamplers = globalSamplers;
this.globalFunctions = globalFunctions;
}
@Override
public NoiseSampler get() {
var mergedFunctions = new HashMap<>(globalFunctions); mergedFunctions.putAll(functions);
var mergedSamplers = new HashMap<>(globalSamplers); mergedSamplers.putAll(samplers);
try {
return new ExpressionNormalizer(function, convertFunctionsAndSamplers(mergedFunctions, mergedSamplers), expression, vars);
} catch(ParseException e) {
throw new RuntimeException("Failed to parse expression.", e);
}
}
}
@@ -0,0 +1,33 @@
package com.dfsek.terra.addons.noise.normalizer;
import com.dfsek.paralithic.Expression;
import com.dfsek.paralithic.eval.parser.Parser;
import com.dfsek.paralithic.eval.parser.Scope;
import com.dfsek.paralithic.eval.tokenizer.ParseException;
import com.dfsek.paralithic.functions.Function;
import com.dfsek.terra.api.noise.NoiseSampler;
import java.util.Map;
public class ExpressionNormalizer extends Normalizer {
private final Expression expression;
public ExpressionNormalizer(NoiseSampler sampler, Map<String, Function> functions, String eq, Map<String, Double> vars)
throws ParseException {
super(sampler);
Parser p = new Parser();
Scope scope = new Scope();
scope.addInvocationVariable("in");
vars.forEach(scope::create);
functions.forEach(p::registerFunction);
expression = p.parse(eq, scope);
}
@Override
public double normalize(double in) {
return expression.evaluate(in);
}
}
@@ -0,0 +1,31 @@
package com.dfsek.terra.addons.noise.paralithic;
import com.dfsek.paralithic.eval.tokenizer.ParseException;
import com.dfsek.paralithic.functions.Function;
import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler;
import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
import com.dfsek.terra.addons.noise.paralithic.defined.UserDefinedFunction;
import com.dfsek.terra.addons.noise.paralithic.noise.NoiseFunction2;
import com.dfsek.terra.addons.noise.paralithic.noise.NoiseFunction3;
import java.util.HashMap;
import java.util.Map;
public class FunctionUtil {
private FunctionUtil() {}
public static Map<String, Function> convertFunctionsAndSamplers(Map<String, FunctionTemplate> functions,
Map<String, DimensionApplicableNoiseSampler> samplers) throws ParseException {
Map<String, Function> functionMap = new HashMap<>();
for(Map.Entry<String, FunctionTemplate> entry : functions.entrySet()) {
functionMap.put(entry.getKey(), UserDefinedFunction.newInstance(entry.getValue()));
}
samplers.forEach((id, sampler) -> functionMap.put(id,
sampler.getDimensions() == 2 ?
new NoiseFunction2(sampler.getSampler()) :
new NoiseFunction3(sampler.getSampler())));
return functionMap;
}
}
@@ -0,0 +1,27 @@
package com.dfsek.terra.addons.noise.samplers;
import com.dfsek.terra.api.noise.NoiseSampler;
public class TranslateSampler implements NoiseSampler {
private final NoiseSampler sampler;
private final double dx, dy, dz;
public TranslateSampler(NoiseSampler sampler, double dx, double dy, double dz) {
this.sampler = sampler;
this.dx = dx;
this.dy = dy;
this.dz = dz;
}
@Override
public double noise(long seed, double x, double y) {
return sampler.noise(seed, x - dx, y - dz);
}
@Override
public double noise(long seed, double x, double y, double z) {
return sampler.noise(seed, x - dx, y - dy, z - dz);
}
}
@@ -0,0 +1,66 @@
package com.dfsek.terra.addons.noise.samplers.noise;
public class DistanceSampler extends NoiseFunction {
private final DistanceFunction distanceFunction;
private final double ox, oy, oz;
private final boolean normalize;
private final double radius;
private final double distanceAtRadius;
public DistanceSampler(DistanceFunction distanceFunction, double ox, double oy, double oz, boolean normalize, double radius) {
frequency = 1;
this.distanceFunction = distanceFunction;
this.ox = ox;
this.oy = oy;
this.oz = oz;
this.normalize = normalize;
this.radius = radius;
this.distanceAtRadius = distance2d(distanceFunction, radius, 0); // distance2d and distance3d should return the same value
}
@Override
public double getNoiseRaw(long seed, double x, double y) {
double dx = x - ox;
double dy = y - oz;
if (normalize && (fastAbs(dx) > radius || fastAbs(dy) > radius)) return 1;
double dist = distance2d(distanceFunction, dx, dy);
if (normalize) return fastMin(((2*dist)/distanceAtRadius)-1, 1);
return dist;
}
@Override
public double getNoiseRaw(long seed, double x, double y, double z) {
double dx = x - ox;
double dy = y - oy;
double dz = z - oz;
if(normalize && (fastAbs(dx) > radius || fastAbs(dy) > radius || fastAbs(dz) > radius)) return 1;
double dist = distance3d(distanceFunction, dx, dy, dz);
if (normalize) return fastMin(((2*dist)/distanceAtRadius)-1, 1);
return dist;
}
private static double distance2d(DistanceFunction distanceFunction, double x, double z) {
return switch(distanceFunction) {
case Euclidean -> fastSqrt(x*x + z*z);
case EuclideanSq -> x*x + z*z;
case Manhattan -> fastAbs(x) + fastAbs(z);
};
}
private static double distance3d(DistanceFunction distanceFunction, double x, double y, double z) {
return switch(distanceFunction) {
case Euclidean -> fastSqrt(x*x + y*y + z*z);
case EuclideanSq -> x*x + y*y + z*z;
case Manhattan -> fastAbs(x) + fastAbs(y) + fastAbs(z);
};
}
public enum DistanceFunction {
Euclidean,
EuclideanSq,
Manhattan
}
}
@@ -5,18 +5,19 @@
* reference the LICENSE file in this module's root directory. * reference the LICENSE file in this module's root directory.
*/ */
package com.dfsek.terra.addons.terrascript.tokenizer; package com.dfsek.terra.addons.terrascript.lexer;
import java.util.Objects;
public class Char { public class Char {
private final char character; private final char character;
private final int index; private final SourcePosition position;
private final int line;
public Char(char character, int index, int line) { public Char(char character, SourcePosition position) {
this.character = character; this.character = character;
this.index = index; this.position = position;
this.line = line;
} }
public boolean is(char... tests) { public boolean is(char... tests) {
@@ -33,18 +34,23 @@ public class Char {
return Character.toString(character); return Character.toString(character);
} }
@Override
public boolean equals(Object o) {
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;
Char other = (Char) o;
return character == other.character && Objects.equals(position, other.position);
}
@Override
public int hashCode() {
return Objects.hash(character, position);
}
public char getCharacter() { public char getCharacter() {
return character; return character;
} }
public int getIndex() {
return index;
}
public int getLine() {
return line;
}
public boolean isWhitespace() { public boolean isWhitespace() {
return Character.isWhitespace(character); return Character.isWhitespace(character);
} }
@@ -0,0 +1,256 @@
/*
* 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.lexer;
import com.google.common.collect.Sets;
import java.util.Arrays;
import java.util.Set;
import java.util.Stack;
import com.dfsek.terra.addons.terrascript.lexer.Token.TokenType;
import com.dfsek.terra.addons.terrascript.lexer.exceptions.EOFException;
import com.dfsek.terra.addons.terrascript.lexer.exceptions.FormatException;
import com.dfsek.terra.addons.terrascript.lexer.exceptions.TokenizerException;
import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException;
public class Lexer {
public static final Set<Character> syntaxSignificant = Sets.newHashSet(';', '(', ')', '"', ',', '\\', '=', '{', '}', '+', '-', '*', '/',
'>', '<', '!'); // Reserved chars
private final LookaheadStream reader;
private final Stack<Token> bracketStack = new Stack<>();
private Token current;
public Lexer(String data) {
reader = new LookaheadStream(data + '\0');
current = tokenize();
}
/**
* Get the first token.
*
* @return First token
*
* @throws ParseException If token does not exist
*/
public Token current() {
return current;
}
/**
* Consume (get and remove) the first token.
*
* @return First token
*
* @throws ParseException If token does not exist
*/
public Token consume(String wrongTypeMessage, TokenType expected, TokenType... more) {
if(!current.isType(expected) && Arrays.stream(more).noneMatch(t -> t == current.getType())) throw new ParseException(wrongTypeMessage, current.getPosition());
return consumeUnchecked();
}
public Token consumeUnchecked() {
if(current.getType() == TokenType.END_OF_FILE) return current;
Token temp = current;
current = tokenize();
return temp;
}
/**
* Whether this {@code Tokenizer} contains additional tokens.
*
* @return {@code true} if more tokens are present, otherwise {@code false}
*/
public boolean hasNext() {
return current.getType() != TokenType.END_OF_FILE;
}
private Token tokenize() throws TokenizerException {
consumeWhitespace();
SourcePosition position = reader.getPosition();
// Skip line if comment
while(reader.matchesString("//", true)) skipLine();
// Skip multi line comment
if(reader.matchesString("/*", true)) skipTo("*/");
// Reached end of file
if(reader.current().isEOF()) {
if(!bracketStack.isEmpty()) throw new ParseException("Dangling open brace", bracketStack.peek().getPosition());
return new Token(reader.consume().toString(), TokenType.END_OF_FILE, position);
}
// Check if operator token
if(reader.matchesString("==", true))
return new Token("==", TokenType.EQUALS_EQUALS, position);
if(reader.matchesString("!=", true))
return new Token("!=", TokenType.BANG_EQUALS, position);
if(reader.matchesString(">=", true))
return new Token(">=", TokenType.GREATER_EQUAL, position);
if(reader.matchesString("<=", true))
return new Token("<=", TokenType.LESS_EQUALS, position);
if(reader.matchesString(">", true))
return new Token(">", TokenType.GREATER, position);
if(reader.matchesString("<", true))
return new Token("<", TokenType.LESS, position);
// Check if logical operator
if(reader.matchesString("||", true))
return new Token("||", TokenType.BOOLEAN_OR, position);
if(reader.matchesString("&&", true))
return new Token("&&", TokenType.BOOLEAN_AND, position);
// Check if number
if(isNumberStart()) {
StringBuilder num = new StringBuilder();
while(!reader.current().isEOF() && isNumberLike()) {
num.append(reader.consume().getCharacter());
}
return new Token(num.toString(), TokenType.NUMBER, position);
}
// Check if string literal
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. ", position);
string.append(reader.consume());
}
reader.consume(); // Consume last quote
return new Token(string.toString(), TokenType.STRING, position);
}
if(reader.current().is('('))
return new Token(reader.consume().toString(), TokenType.OPEN_PAREN, position);
if(reader.current().is(')'))
return new Token(reader.consume().toString(), TokenType.CLOSE_PAREN, position);
if(reader.current().is(';'))
return new Token(reader.consume().toString(), TokenType.STATEMENT_END, position);
if(reader.current().is(','))
return new Token(reader.consume().toString(), TokenType.SEPARATOR, position);
if(reader.current().is('{')) {
Token token = new Token(reader.consume().toString(), TokenType.BLOCK_BEGIN, position);
bracketStack.push(token);
return token;
}
if(reader.current().is('}')) {
if(bracketStack.isEmpty()) throw new ParseException("Dangling close brace", position);
bracketStack.pop();
return new Token(reader.consume().toString(), TokenType.BLOCK_END, position);
}
if(reader.current().is('='))
return new Token(reader.consume().toString(), TokenType.ASSIGNMENT, position);
if(reader.current().is('+'))
return new Token(reader.consume().toString(), TokenType.PLUS, position);
if(reader.current().is('-'))
return new Token(reader.consume().toString(), TokenType.MINUS,
position);
if(reader.current().is('*'))
return new Token(reader.consume().toString(), TokenType.STAR,
position);
if(reader.current().is('/'))
return new Token(reader.consume().toString(), TokenType.FORWARD_SLASH, position);
if(reader.current().is('%'))
return new Token(reader.consume().toString(), TokenType.MODULO_OPERATOR, position);
if(reader.current().is('!'))
return new Token(reader.consume().toString(), TokenType.BANG, position);
// Read word
StringBuilder token = new StringBuilder();
while(!reader.current().isEOF() && !isSyntaxSignificant(reader.current().getCharacter())) {
Char c = reader.consume();
if(c.isWhitespace()) break;
token.append(c.getCharacter());
}
String tokenString = token.toString();
// Check if word is a keyword
if(tokenString.equals("true"))
return new Token(tokenString, TokenType.BOOLEAN, position);
if(tokenString.equals("false"))
return new Token(tokenString, TokenType.BOOLEAN, position);
if(tokenString.equals("num"))
return new Token(tokenString, TokenType.TYPE_NUMBER, position);
if(tokenString.equals("str"))
return new Token(tokenString, TokenType.TYPE_STRING, position);
if(tokenString.equals("bool"))
return new Token(tokenString, TokenType.TYPE_BOOLEAN, position);
if(tokenString.equals("void"))
return new Token(tokenString, TokenType.TYPE_VOID, position);
if(tokenString.equals("if"))
return new Token(tokenString, TokenType.IF_STATEMENT, position);
if(tokenString.equals("else"))
return new Token(tokenString, TokenType.ELSE, position);
if(tokenString.equals("while"))
return new Token(tokenString, TokenType.WHILE_LOOP, position);
if(tokenString.equals("for"))
return new Token(tokenString, TokenType.FOR_LOOP, position);
if(tokenString.equals("return"))
return new Token(tokenString, TokenType.RETURN, position);
if(tokenString.equals("continue"))
return new Token(tokenString, TokenType.CONTINUE, position);
if(tokenString.equals("break"))
return new Token(tokenString, TokenType.BREAK, position);
if(tokenString.equals("fail"))
return new Token(tokenString, TokenType.FAIL, position);
// If not keyword, assume it is an identifier
return new Token(tokenString, TokenType.IDENTIFIER, position);
}
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 {
SourcePosition begin = reader.getPosition();
while(!reader.current().isEOF()) {
if(reader.matchesString(s, true)) {
consumeWhitespace();
return;
}
reader.consume();
}
throw new EOFException("No end of expression found.", begin);
}
private boolean isNumberLike() {
return reader.current().isDigit()
|| reader.current().is('_', '.', 'E');
}
private boolean isNumberStart() {
return reader.current().isDigit()
|| reader.current().is('.') && reader.peek().isDigit();
}
public boolean isSyntaxSignificant(char c) {
return syntaxSignificant.contains(c);
}
}
@@ -0,0 +1,84 @@
package com.dfsek.terra.addons.terrascript.lexer;
public class LookaheadStream {
private final String source;
private int index;
private SourcePosition position = new SourcePosition(1, 1);
public LookaheadStream(String source) {
this.source = source;
}
/**
* Get the current character without consuming it.
*
* @return current character
*/
public Char current() {
return new Char(source.charAt(index), position);
}
/**
* Consume and return one character.
*
* @return Character that was consumed.
*/
public Char consume() {
Char consumed = current();
incrementIndex(1);
return consumed;
}
/**
* @return The next character in sequence.
*/
public Char peek() {
int index = this.index + 1;
if (index + 1 >= source.length()) return null;
return new Char(source.charAt(index), getPositionAfter(1));
}
/**
* Determines if the contained sequence of characters matches the string
*
* @param check Input string to check against
* @param consumeIfMatched Whether to consume the string if there is a match
* @return If the string matches
*/
public boolean matchesString(String check, boolean consumeIfMatched) {
boolean matches = check.equals(source.substring(index, Math.min(index + check.length(), source.length())));
if (matches && consumeIfMatched) incrementIndex(check.length());
return matches;
}
/**
* @return Current position within the source file
*/
public SourcePosition getPosition() {
return position;
}
private void incrementIndex(int amount) {
position = getPositionAfter(amount);
index = Math.min(index + amount, source.length() - 1);
}
private SourcePosition getPositionAfter(int chars) {
if (chars < 0) throw new IllegalArgumentException("Negative values are not allowed");
int line = position.line();
int column = position.column();
for (int i = index; i < Math.min(index + chars, source.length() - 1); i++) {
if (source.charAt(i) == '\n') {
line++;
column = 0;
}
column++;
}
return new SourcePosition(line, column);
}
}
@@ -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.lexer;
import java.util.Objects;
public record SourcePosition(int line, int column) {
@Override
public String toString() {
return "line " + line + ", column " + column;
}
@Override
public boolean equals(Object o) {
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;
SourcePosition that = (SourcePosition) o;
return line == that.line && column == that.column;
}
@Override
public int hashCode() {
return Objects.hash(line, column);
}
}
@@ -5,14 +5,14 @@
* reference the LICENSE file in this module's root directory. * reference the LICENSE file in this module's root directory.
*/ */
package com.dfsek.terra.addons.terrascript.tokenizer; package com.dfsek.terra.addons.terrascript.lexer;
public class Token { public class Token {
private final String content; private final String content;
private final Type type; private final TokenType type;
private final Position start; private final SourcePosition start;
public Token(String content, Type type, Position start) { public Token(String content, TokenType type, SourcePosition start) {
this.content = content; this.content = content;
this.type = type; this.type = type;
this.start = start; this.start = start;
@@ -23,7 +23,7 @@ public class Token {
return type + ": '" + content + "'"; return type + ": '" + content + "'";
} }
public Type getType() { public TokenType getType() {
return type; return type;
} }
@@ -31,63 +31,68 @@ public class Token {
return content; return content;
} }
public Position getPosition() { public SourcePosition getPosition() {
return start; return start;
} }
public boolean isConstant() { public boolean isConstant() {
return this.type.equals(Type.NUMBER) || this.type.equals(Type.STRING) || this.type.equals(Type.BOOLEAN); return this.type.equals(TokenType.NUMBER) || this.type.equals(TokenType.STRING) || this.type.equals(TokenType.BOOLEAN);
}
public boolean isType(TokenType type) {
return type == getType();
}
public boolean isType(TokenType... types) {
for (TokenType t : types) if (isType(t)) return true;
return false;
} }
public boolean isBinaryOperator() { public boolean isBinaryOperator() {
return type.equals(Type.ADDITION_OPERATOR) return type.equals(TokenType.PLUS)
|| type.equals(Type.SUBTRACTION_OPERATOR) || type.equals(TokenType.MINUS)
|| type.equals(Type.MULTIPLICATION_OPERATOR) || type.equals(TokenType.STAR)
|| type.equals(Type.DIVISION_OPERATOR) || type.equals(TokenType.FORWARD_SLASH)
|| type.equals(Type.EQUALS_OPERATOR) || type.equals(TokenType.EQUALS_EQUALS)
|| type.equals(Type.NOT_EQUALS_OPERATOR) || type.equals(TokenType.BANG_EQUALS)
|| type.equals(Type.LESS_THAN_OPERATOR) || type.equals(TokenType.LESS)
|| type.equals(Type.GREATER_THAN_OPERATOR) || type.equals(TokenType.GREATER)
|| type.equals(Type.LESS_THAN_OR_EQUALS_OPERATOR) || type.equals(TokenType.LESS_EQUALS)
|| type.equals(Type.GREATER_THAN_OR_EQUALS_OPERATOR) || type.equals(TokenType.GREATER_EQUAL)
|| type.equals(Type.BOOLEAN_OR) || type.equals(TokenType.BOOLEAN_OR)
|| type.equals(Type.BOOLEAN_AND) || type.equals(TokenType.BOOLEAN_AND)
|| type.equals(Type.MODULO_OPERATOR); || type.equals(TokenType.MODULO_OPERATOR);
} }
public boolean isStrictNumericOperator() { public boolean isStrictNumericOperator() {
return type.equals(Type.SUBTRACTION_OPERATOR) return type.equals(TokenType.MINUS)
|| type.equals(Type.MULTIPLICATION_OPERATOR) || type.equals(TokenType.STAR)
|| type.equals(Type.DIVISION_OPERATOR) || type.equals(TokenType.FORWARD_SLASH)
|| type.equals(Type.GREATER_THAN_OPERATOR) || type.equals(TokenType.GREATER)
|| type.equals(Type.LESS_THAN_OPERATOR) || type.equals(TokenType.LESS)
|| type.equals(Type.LESS_THAN_OR_EQUALS_OPERATOR) || type.equals(TokenType.LESS_EQUALS)
|| type.equals(Type.GREATER_THAN_OR_EQUALS_OPERATOR) || type.equals(TokenType.GREATER_EQUAL)
|| type.equals(Type.MODULO_OPERATOR); || type.equals(TokenType.MODULO_OPERATOR);
} }
public boolean isStrictBooleanOperator() { public boolean isStrictBooleanOperator() {
return type.equals(Type.BOOLEAN_AND) return type.equals(TokenType.BOOLEAN_AND)
|| type.equals(Type.BOOLEAN_OR); || type.equals(TokenType.BOOLEAN_OR);
} }
public boolean isVariableDeclaration() { public boolean isVariableDeclaration() {
return type.equals(Type.STRING_VARIABLE) return type.equals(TokenType.TYPE_STRING)
|| type.equals(Type.BOOLEAN_VARIABLE) || type.equals(TokenType.TYPE_BOOLEAN)
|| type.equals(Type.NUMBER_VARIABLE); || type.equals(TokenType.TYPE_NUMBER);
} }
public boolean isLoopLike() { public boolean isControlStructure() {
return type.equals(Type.IF_STATEMENT) return type.equals(TokenType.IF_STATEMENT)
|| type.equals(Type.WHILE_LOOP) || type.equals(TokenType.WHILE_LOOP)
|| type.equals(Type.FOR_LOOP); || type.equals(TokenType.FOR_LOOP);
} }
public boolean isIdentifier() { public enum TokenType {
return type.equals(Type.IDENTIFIER);
}
public enum Type {
/** /**
* Function identifier or language keyword * Function identifier or language keyword
*/ */
@@ -108,11 +113,11 @@ public class Token {
/** /**
* Beginning of group * Beginning of group
*/ */
GROUP_BEGIN, OPEN_PAREN,
/** /**
* Ending of group * Ending of group
*/ */
GROUP_END, CLOSE_PAREN,
/** /**
* End of statement * End of statement
*/ */
@@ -136,43 +141,43 @@ public class Token {
/** /**
* Boolean equals operator * Boolean equals operator
*/ */
EQUALS_OPERATOR, EQUALS_EQUALS,
/** /**
* Boolean not equals operator * Boolean not equals operator
*/ */
NOT_EQUALS_OPERATOR, BANG_EQUALS,
/** /**
* Boolean greater than operator * Boolean greater than operator
*/ */
GREATER_THAN_OPERATOR, GREATER,
/** /**
* Boolean less than operator * Boolean less than operator
*/ */
LESS_THAN_OPERATOR, LESS,
/** /**
* Boolean greater than or equal to operator * Boolean greater than or equal to operator
*/ */
GREATER_THAN_OR_EQUALS_OPERATOR, GREATER_EQUAL,
/** /**
* Boolean less than or equal to operator * Boolean less than or equal to operator
*/ */
LESS_THAN_OR_EQUALS_OPERATOR, LESS_EQUALS,
/** /**
* Addition/concatenation operator * Addition/concatenation operator
*/ */
ADDITION_OPERATOR, PLUS,
/** /**
* Subtraction operator * Subtraction operator
*/ */
SUBTRACTION_OPERATOR, MINUS,
/** /**
* Multiplication operator * Multiplication operator
*/ */
MULTIPLICATION_OPERATOR, STAR,
/** /**
* Division operator * Division operator
*/ */
DIVISION_OPERATOR, FORWARD_SLASH,
/** /**
* Modulo operator. * Modulo operator.
*/ */
@@ -180,7 +185,7 @@ public class Token {
/** /**
* Boolean not operator * Boolean not operator
*/ */
BOOLEAN_NOT, BANG,
/** /**
* Boolean or * Boolean or
*/ */
@@ -192,15 +197,19 @@ public class Token {
/** /**
* Numeric variable declaration * Numeric variable declaration
*/ */
NUMBER_VARIABLE, TYPE_NUMBER,
/** /**
* String variable declaration * String variable declaration
*/ */
STRING_VARIABLE, TYPE_STRING,
/** /**
* Boolean variable declaration * Boolean variable declaration
*/ */
BOOLEAN_VARIABLE, TYPE_BOOLEAN,
/**
* Void type declaration
*/
TYPE_VOID,
/** /**
* If statement declaration * If statement declaration
*/ */
@@ -232,6 +241,10 @@ public class Token {
/** /**
* Else keyword * Else keyword
*/ */
ELSE ELSE,
/**
* End of file
*/
END_OF_FILE
} }
} }
@@ -5,11 +5,11 @@
* reference the LICENSE file in this module's root directory. * reference the LICENSE file in this module's root directory.
*/ */
package com.dfsek.terra.addons.terrascript.tokenizer.exceptions; package com.dfsek.terra.addons.terrascript.lexer.exceptions;
import java.io.Serial; import java.io.Serial;
import com.dfsek.terra.addons.terrascript.tokenizer.Position; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
public class EOFException extends TokenizerException { public class EOFException extends TokenizerException {
@@ -17,11 +17,11 @@ public class EOFException extends TokenizerException {
@Serial @Serial
private static final long serialVersionUID = 3980047409902809440L; private static final long serialVersionUID = 3980047409902809440L;
public EOFException(String message, Position position) { public EOFException(String message, SourcePosition position) {
super(message, position); super(message, position);
} }
public EOFException(String message, Position position, Throwable cause) { public EOFException(String message, SourcePosition position, Throwable cause) {
super(message, position, cause); super(message, position, cause);
} }
} }
@@ -5,11 +5,11 @@
* reference the LICENSE file in this module's root directory. * reference the LICENSE file in this module's root directory.
*/ */
package com.dfsek.terra.addons.terrascript.tokenizer.exceptions; package com.dfsek.terra.addons.terrascript.lexer.exceptions;
import java.io.Serial; import java.io.Serial;
import com.dfsek.terra.addons.terrascript.tokenizer.Position; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
public class FormatException extends TokenizerException { public class FormatException extends TokenizerException {
@@ -17,11 +17,11 @@ public class FormatException extends TokenizerException {
@Serial @Serial
private static final long serialVersionUID = -791308012940744455L; private static final long serialVersionUID = -791308012940744455L;
public FormatException(String message, Position position) { public FormatException(String message, SourcePosition position) {
super(message, position); super(message, position);
} }
public FormatException(String message, Position position, Throwable cause) { public FormatException(String message, SourcePosition position, Throwable cause) {
super(message, position, cause); super(message, position, cause);
} }
} }
@@ -5,12 +5,12 @@
* reference the LICENSE file in this module's root directory. * reference the LICENSE file in this module's root directory.
*/ */
package com.dfsek.terra.addons.terrascript.tokenizer.exceptions; package com.dfsek.terra.addons.terrascript.lexer.exceptions;
import java.io.Serial; import java.io.Serial;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public abstract class TokenizerException extends ParseException { public abstract class TokenizerException extends ParseException {
@@ -18,11 +18,11 @@ public abstract class TokenizerException extends ParseException {
@Serial @Serial
private static final long serialVersionUID = 2792384010083575420L; private static final long serialVersionUID = 2792384010083575420L;
public TokenizerException(String message, Position position) { public TokenizerException(String message, SourcePosition position) {
super(message, position); super(message, position);
} }
public TokenizerException(String message, Position position, Throwable cause) { public TokenizerException(String message, SourcePosition position, Throwable cause) {
super(message, position, cause); super(message, position, cause);
} }
} }
@@ -9,24 +9,26 @@ package com.dfsek.terra.addons.terrascript.parser;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import com.dfsek.terra.addons.terrascript.lexer.Lexer;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.lexer.Token;
import com.dfsek.terra.addons.terrascript.lexer.Token.TokenType;
import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; 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.Block;
import com.dfsek.terra.addons.terrascript.parser.lang.Executable; 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.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.Keyword; import com.dfsek.terra.addons.terrascript.parser.lang.Expression.ReturnType;
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.Scope.ScopeBuilder;
import com.dfsek.terra.addons.terrascript.parser.lang.constants.BooleanConstant; 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.NumericConstant;
import com.dfsek.terra.addons.terrascript.parser.lang.constants.StringConstant; 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.functions.FunctionBuilder;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.UserDefinedFunctionBuilder;
import com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow.BreakKeyword; 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.ContinueKeyword;
import com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow.FailKeyword; import com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow.FailKeyword;
@@ -34,7 +36,6 @@ import com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow.ReturnKeywor
import com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike.ForKeyword; 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.IfKeyword;
import com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike.WhileKeyword; 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.BooleanAndOperation;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BooleanNotOperation; 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.BooleanOrOperation;
@@ -58,30 +59,16 @@ import com.dfsek.terra.addons.terrascript.parser.lang.variables.assign.VariableA
import com.dfsek.terra.addons.terrascript.parser.lang.variables.reference.BoolVariableReferenceNode; 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.NumVariableReferenceNode;
import com.dfsek.terra.addons.terrascript.parser.lang.variables.reference.StrVariableReferenceNode; 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; import com.dfsek.terra.api.util.generic.pair.Pair;
@SuppressWarnings("unchecked")
public class Parser { public class Parser {
private final String data;
private final Map<String, FunctionBuilder<? extends Function<?>>> functions = new HashMap<>();
private final List<String> ignoredFunctions = new ArrayList<>(); private final List<String> ignoredFunctions = new ArrayList<>();
public Parser(String data) { private final Lexer lexer;
this.data = data;
}
public Parser registerFunction(String name, FunctionBuilder<? extends Function<?>> functionBuilder) { public Parser(Lexer lexer) {
functions.put(name, functionBuilder); this.lexer = lexer;
return this;
}
public Parser ignoreFunction(String name) {
ignoredFunctions.add(name);
return this;
} }
/** /**
@@ -91,378 +78,447 @@ public class Parser {
* *
* @throws ParseException If parsing fails. * @throws ParseException If parsing fails.
*/ */
public Executable parse() { public Executable parse(ScopeBuilder scopeBuilder) {
ScopeBuilder scopeBuilder = new ScopeBuilder(); return new Executable(parseBlock(scopeBuilder, ReturnType.VOID), scopeBuilder);
return new Executable(parseBlock(new Tokenizer(data), false, scopeBuilder), scopeBuilder);
} }
private Keyword<?> parseLoopLike(Tokenizer tokens, boolean loop, ScopeBuilder scopeBuilder) throws ParseException { private WhileKeyword parseWhileLoop(ScopeBuilder scopeBuilder) {
SourcePosition start = lexer.consume("Expected 'while' keyword at beginning of while loop", TokenType.WHILE_LOOP).getPosition();
Token identifier = tokens.consume(); lexer.consume("Expected '(' proceeding 'while' keyword", TokenType.OPEN_PAREN);
ParserUtil.checkType(identifier, Token.Type.IF_STATEMENT, Token.Type.WHILE_LOOP, Token.Type.FOR_LOOP); scopeBuilder = scopeBuilder.innerLoopScope();
Expression<?> condition = parseExpression(scopeBuilder);
ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_BEGIN); ParserUtil.ensureReturnType(condition, Expression.ReturnType.BOOLEAN);
lexer.consume("Expected ')' proceeding while loop condition", TokenType.CLOSE_PAREN);
return switch(identifier.getType()) { return new WhileKeyword(parseStatementBlock(scopeBuilder, ReturnType.VOID), (Expression<Boolean>) condition,
case FOR_LOOP -> parseForLoop(tokens, identifier.getPosition(), scopeBuilder); start); // While loop
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) { private IfKeyword parseIfStatement(ScopeBuilder scopeBuilder) {
Returnable<?> first = parseExpression(tokens, true, scopeBuilder); SourcePosition start = lexer.consume("Expected 'if' keyword at beginning of if statement", TokenType.IF_STATEMENT).getPosition();
ParserUtil.checkReturnType(first, Returnable.ReturnType.BOOLEAN); lexer.consume("Expected '(' proceeding 'if' keyword", TokenType.OPEN_PAREN);
Expression<?> condition = parseExpression(scopeBuilder);
ParserUtil.ensureReturnType(condition, Expression.ReturnType.BOOLEAN);
ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_END); lexer.consume("Expected ')' proceeding if statement condition", TokenType.CLOSE_PAREN);
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 elseBlock = null;
Block statement = parseStatementBlock(tokens, loop, scopeBuilder); Block statement = parseStatementBlock(scopeBuilder, ReturnType.VOID);
List<Pair<Returnable<Boolean>, Block>> elseIf = new ArrayList<>(); List<Pair<Expression<Boolean>, Block>> elseIf = new ArrayList<>();
while(tokens.hasNext() && tokens.get().getType().equals(Token.Type.ELSE)) { while(lexer.hasNext() && lexer.current().isType(TokenType.ELSE)) {
tokens.consume(); // Consume else. lexer.consumeUnchecked(); // Consume else.
if(tokens.get().getType().equals(Token.Type.IF_STATEMENT)) { if(lexer.current().isType(TokenType.IF_STATEMENT)) {
tokens.consume(); // Consume if. lexer.consumeUnchecked(); // Consume if.
Returnable<?> elseCondition = parseExpression(tokens, true, scopeBuilder); Expression<?> elseCondition = parseExpression(scopeBuilder);
ParserUtil.checkReturnType(elseCondition, Returnable.ReturnType.BOOLEAN); ParserUtil.ensureReturnType(elseCondition, Expression.ReturnType.BOOLEAN);
elseIf.add(Pair.of((Returnable<Boolean>) elseCondition, parseStatementBlock(tokens, loop, scopeBuilder))); elseIf.add(Pair.of((Expression<Boolean>) elseCondition, parseStatementBlock(scopeBuilder, ReturnType.VOID)));
} else { } else {
elseBlock = parseStatementBlock(tokens, loop, scopeBuilder); elseBlock = parseStatementBlock(scopeBuilder, ReturnType.VOID);
break; // Else must be last. break; // Else must be last.
} }
} }
return new IfKeyword(statement, (Returnable<Boolean>) condition, elseIf, elseBlock, start); // If statement return new IfKeyword(statement, (Expression<Boolean>) condition, elseIf, elseBlock, start); // If statement
} }
private Block parseStatementBlock(Tokenizer tokens, boolean loop, ScopeBuilder scopeBuilder) { private Block parseStatementBlock(ScopeBuilder scopeBuilder, ReturnType blockReturnType) {
if(lexer.current().isType(TokenType.BLOCK_BEGIN)) {
if(tokens.get().getType().equals(Token.Type.BLOCK_BEGIN)) { lexer.consumeUnchecked();
ParserUtil.checkType(tokens.consume(), Token.Type.BLOCK_BEGIN); Block block = parseBlock(scopeBuilder, blockReturnType);
Block block = parseBlock(tokens, loop, scopeBuilder); lexer.consume("Expected block end '}' after block statements", TokenType.BLOCK_END);
ParserUtil.checkType(tokens.consume(), Token.Type.BLOCK_END);
return block; return block;
} else { } else {
Position position = tokens.get().getPosition(); SourcePosition position = lexer.current().getPosition();
Block block = new Block(Collections.singletonList(parseItem(tokens, loop, scopeBuilder)), position); return new Block(Collections.singletonList(parseStatement(scopeBuilder)), position, blockReturnType);
ParserUtil.checkType(tokens.consume(), Token.Type.STATEMENT_END);
return block;
} }
} }
private ForKeyword parseForLoop(Tokenizer tokens, Position start, ScopeBuilder scopeBuilder) { private ForKeyword parseForLoop(ScopeBuilder scopeBuilder) {
scopeBuilder = scopeBuilder.sub(); // new scope SourcePosition start = lexer.consume("Expected 'for' keyword at beginning of for loop", TokenType.FOR_LOOP).getPosition();
Token f = tokens.get(); lexer.consume("Expected '(' after 'for' keyword", TokenType.OPEN_PAREN);
ParserUtil.checkType(f, Token.Type.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, Token.Type.IDENTIFIER); scopeBuilder = scopeBuilder.innerLoopScope(); // new scope
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; Expression<?> initializer = switch(lexer.current().getType()) {
Token token = tokens.get(); case TYPE_NUMBER, TYPE_STRING, TYPE_BOOLEAN -> {
if(scopeBuilder.contains(token.getContent())) { // Assume variable assignment Token type = lexer.consume("Expected type before declaration", TokenType.TYPE_STRING, TokenType.TYPE_NUMBER, TokenType.TYPE_BOOLEAN, TokenType.TYPE_VOID);
incrementer = parseAssignment(tokens, scopeBuilder); Token identifier = lexer.consume("Expected identifier after type", TokenType.IDENTIFIER);
} else incrementer = parseFunction(tokens, true, scopeBuilder); Expression<?> expr = parseVariableDeclaration(scopeBuilder, type, identifier);
lexer.consume("Expected ';' after initializer within for loop", TokenType.STATEMENT_END);
yield expr;
}
case IDENTIFIER -> {
Expression<?> expr = parseAssignment(scopeBuilder);
lexer.consume("Expected ';' after initializer within for loop", TokenType.STATEMENT_END);
yield expr;
}
case STATEMENT_END -> {
lexer.consumeUnchecked();
yield Expression.NOOP;
}
default -> throw new ParseException("Unexpected token '" + lexer.current() + "', expected variable declaration or assignment", lexer.current().getPosition());
};
ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_END); Expression<?> conditional;
if (lexer.current().isType(TokenType.STATEMENT_END)) // If no conditional is provided, conditional defaults to true
conditional = new BooleanConstant(true, lexer.current().getPosition());
else
conditional = parseExpression(scopeBuilder);
ParserUtil.ensureReturnType(conditional, Expression.ReturnType.BOOLEAN);
lexer.consume("Expected ';' separator after conditional within for loop", TokenType.STATEMENT_END);
return new ForKeyword(parseStatementBlock(tokens, true, scopeBuilder), initializer, (Returnable<Boolean>) conditional, incrementer, Expression<?> incrementer;
if(lexer.current().isType(TokenType.CLOSE_PAREN))
// If no incrementer is provided, do nothing
incrementer = Expression.NOOP;
else if(scopeBuilder.containsVariable(lexer.current().getContent())) // Assume variable assignment
incrementer = parseAssignment(scopeBuilder);
else
incrementer = parseFunctionInvocation(lexer.consume("Expected function call within for loop incrementer", TokenType.IDENTIFIER), scopeBuilder);
lexer.consume("Expected ')' after for loop incrementer", TokenType.CLOSE_PAREN);
return new ForKeyword(parseStatementBlock(scopeBuilder, ReturnType.VOID), initializer, (Expression<Boolean>) conditional,
incrementer,
start); start);
} }
private Returnable<?> parseExpression(Tokenizer tokens, boolean full, ScopeBuilder scopeBuilder) { private Expression<?> parseExpression(ScopeBuilder scopeBuilder) {
boolean booleanInverted = false; // Check for boolean not operator return parseLogicOr(scopeBuilder);
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) { private Expression<?> parseLogicOr(ScopeBuilder scopeBuilder) {
Token constantToken = tokens.consume(); return parseLeftAssociativeBinaryOperation(this::parseLogicAnd, scopeBuilder, (op) -> {
Position position = constantToken.getPosition(); ParserUtil.ensureReturnType(op.left, ReturnType.BOOLEAN);
switch(constantToken.getType()) { ParserUtil.ensureReturnType(op.right, ReturnType.BOOLEAN);
case NUMBER: }, Map.of(TokenType.BOOLEAN_OR, (op) -> new BooleanOrOperation((Expression<Boolean>) op.left, (Expression<Boolean>) op.right, op.operator.getPosition())));
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) { private Expression<?> parseLogicAnd(ScopeBuilder scopeBuilder) {
ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_BEGIN); return parseLeftAssociativeBinaryOperation(this::parseEquality, scopeBuilder, (op) -> {
Returnable<?> expression = parseExpression(tokens, true, scopeBuilder); // Parse inside of group as a separate expression ParserUtil.ensureReturnType(op.left, ReturnType.BOOLEAN);
ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_END); ParserUtil.ensureReturnType(op.right, ReturnType.BOOLEAN);
return expression; }, Map.of(TokenType.BOOLEAN_AND, (op) -> new BooleanAndOperation((Expression<Boolean>) op.left, (Expression<Boolean>) op.right, op.operator.getPosition())));
} }
private BinaryOperation<?, ?> parseBinaryOperation(Returnable<?> left, Tokenizer tokens, private Expression<?> parseEquality(ScopeBuilder scopeBuilder) {
ScopeBuilder scopeBuilder) { return parseLeftAssociativeBinaryOperation(this::parseComparison, scopeBuilder, Map.of(
Token binaryOperator = tokens.consume(); TokenType.EQUALS_EQUALS, (op) -> new EqualsStatement((Expression<Object>) op.left, (Expression<Object>) op.right, op.operator.getPosition()),
ParserUtil.checkBinaryOperator(binaryOperator); TokenType.BANG_EQUALS, (op) -> new NotEqualsStatement((Expression<Object>) op.left, (Expression<Object>) op.right, op.operator.getPosition())
));
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) { private Expression<?> parseComparison(ScopeBuilder scopeBuilder) {
if(binaryOperator.isStrictNumericOperator()) return parseLeftAssociativeBinaryOperation(this::parseTerm, scopeBuilder, (op) -> {
ParserUtil.checkArithmeticOperation(left, right, binaryOperator); // Numeric type checking ParserUtil.ensureReturnType(op.left, ReturnType.NUMBER);
if(binaryOperator.isStrictBooleanOperator()) ParserUtil.checkBooleanOperation(left, right, binaryOperator); // Boolean type checking ParserUtil.ensureReturnType(op.right, ReturnType.NUMBER);
switch(binaryOperator.getType()) { }, Map.of(
case ADDITION_OPERATOR: TokenType.LESS, (op) -> new LessThanStatement((Expression<Number>) op.left, (Expression<Number>) op.right, op.operator.getPosition()),
if(left.returnType().equals(Returnable.ReturnType.NUMBER) && right.returnType().equals(Returnable.ReturnType.NUMBER)) { TokenType.LESS_EQUALS, (op) -> new LessThanOrEqualsStatement((Expression<Number>) op.left, (Expression<Number>) op.right, op.operator.getPosition()),
return new NumberAdditionOperation((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition()); TokenType.GREATER, (op) -> new GreaterThanStatement((Expression<Number>) op.left, (Expression<Number>) op.right, op.operator.getPosition()),
TokenType.GREATER_EQUAL, (op) -> new GreaterOrEqualsThanStatement((Expression<Number>) op.left, (Expression<Number>) op.right, op.operator.getPosition())
));
}
private Expression<?> parseTerm(ScopeBuilder scopeBuilder) {
return parseLeftAssociativeBinaryOperation(this::parseFactor, scopeBuilder, Map.of(
TokenType.MINUS, (op) -> {
ParserUtil.ensureReturnType(op.left, ReturnType.NUMBER);
ParserUtil.ensureReturnType(op.right, ReturnType.NUMBER);
return new SubtractionOperation((Expression<Number>) op.left, (Expression<Number>) op.right, op.operator.getPosition());
},
TokenType.PLUS, (op) -> {
if (op.left.returnType() == ReturnType.NUMBER && op.right.returnType() == ReturnType.NUMBER)
return new NumberAdditionOperation((Expression<Number>) op.left, (Expression<Number>) op.right, op.operator.getPosition());
else
return new ConcatenationOperation((Expression<Object>) op.left, (Expression<Object>) op.right, op.operator.getPosition());
}));
}
private Expression<?> parseFactor(ScopeBuilder scopeBuilder) {
return parseLeftAssociativeBinaryOperation(this::parseUnary, scopeBuilder, (op) -> {
ParserUtil.ensureReturnType(op.left, ReturnType.NUMBER);
ParserUtil.ensureReturnType(op.right, ReturnType.NUMBER);
}, Map.of(
TokenType.STAR, (op) -> new MultiplicationOperation((Expression<Number>) op.left, (Expression<Number>) op.right, op.operator.getPosition()),
TokenType.FORWARD_SLASH, (op) -> new DivisionOperation((Expression<Number>) op.left, (Expression<Number>) op.right, op.operator.getPosition()),
TokenType.MODULO_OPERATOR, (op) -> new ModuloOperation((Expression<Number>) op.left, (Expression<Number>) op.right, op.operator.getPosition())
));
}
private Expression<?> parseUnary(ScopeBuilder scopeBuilder) {
if (lexer.current().isType(TokenType.BANG, TokenType.MINUS)) {
Token operator = lexer.consumeUnchecked();
Expression<?> right = parseUnary(scopeBuilder);
return switch(operator.getType()) {
case BANG -> {
ParserUtil.ensureReturnType(right, ReturnType.BOOLEAN);
yield new BooleanNotOperation((Expression<Boolean>) right, operator.getPosition());
} }
return new ConcatenationOperation((Returnable<Object>) left, (Returnable<Object>) right, binaryOperator.getPosition()); case MINUS -> {
case SUBTRACTION_OPERATOR: ParserUtil.ensureReturnType(right, ReturnType.NUMBER);
return new SubtractionOperation((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition()); yield new NegationOperation((Expression<Number>) right, operator.getPosition());
case MULTIPLICATION_OPERATOR: }
return new MultiplicationOperation((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition()); default -> throw new IllegalStateException();
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());
} }
return parsePrimary(scopeBuilder);
} }
private VariableAssignmentNode<?> parseVariableDeclaration(Tokenizer tokens, ScopeBuilder scopeBuilder) { private Expression<?> parsePrimary(ScopeBuilder scopeBuilder) {
Token type = tokens.consume(); Token token = lexer.consumeUnchecked();
ParserUtil.checkType(type, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, Token.Type.NUMBER_VARIABLE); return switch(token.getType()) {
case NUMBER -> {
String content = token.getContent();
yield new NumericConstant(content.contains(".") ? Double.parseDouble(content) : Integer.parseInt(content), token.getPosition());
}
case STRING -> new StringConstant(token.getContent(), token.getPosition());
case BOOLEAN -> new BooleanConstant(Boolean.parseBoolean(token.getContent()), token.getPosition());
case OPEN_PAREN -> {
Expression<?> expr = parseExpression(scopeBuilder);
lexer.consume("Missing ')' at end of expression group", TokenType.CLOSE_PAREN);
yield expr;
}
case IDENTIFIER -> {
if (scopeBuilder.containsFunction(token.getContent()))
yield parseFunctionInvocation(token, scopeBuilder);
else if (scopeBuilder.containsVariable(token.getContent())) {
ReturnType variableType = scopeBuilder.getVaraibleType(token.getContent());
yield switch(variableType) {
case NUMBER -> new NumVariableReferenceNode(token.getPosition(), variableType, scopeBuilder.getIndex(token.getContent()));
case BOOLEAN -> new BoolVariableReferenceNode(token.getPosition(), variableType, scopeBuilder.getIndex(token.getContent()));
case STRING -> new StrVariableReferenceNode(token.getPosition(), variableType, scopeBuilder.getIndex(token.getContent()));
default -> throw new ParseException("Illegal type for variable reference: " + variableType, token.getPosition());
};
}
throw new ParseException("Identifier '" + token.getContent() + "' is not defined in this scope", token.getPosition());
}
default -> throw new ParseException("Unexpected token '" + token.getContent() + "' when parsing expression", token.getPosition());
};
}
private Expression<?> parseLeftAssociativeBinaryOperation(Function<ScopeBuilder, Expression<?>> higherPrecedence, ScopeBuilder scopeBuilder,
Consumer<BinaryOperationInfo> init,
Map<TokenType, Function<BinaryOperationInfo, Expression<?>>> operators) {
Expression<?> expr = higherPrecedence.apply(scopeBuilder);
TokenType[] opTypes = operators.keySet().toArray(new TokenType[0]);
while (lexer.current().isType(opTypes)) {
Token operator = lexer.consumeUnchecked();
Expression<?> right = higherPrecedence.apply(scopeBuilder);
BinaryOperationInfo op = new BinaryOperationInfo(expr, operator, right);
init.accept(op);
expr = operators.get(operator.getType()).apply(op);
}
return expr;
}
private Expression<?> parseLeftAssociativeBinaryOperation(Function<ScopeBuilder, Expression<?>> higherPrecedence, ScopeBuilder scopeBuilder, Map<TokenType, Function<BinaryOperationInfo, Expression<?>>> operators) {
return parseLeftAssociativeBinaryOperation(higherPrecedence, scopeBuilder, (op) -> {}, operators);
}
private record BinaryOperationInfo(Expression<?> left, Token operator, Expression<?> right) {}
private Expression<?> parseDeclaration(ScopeBuilder scopeBuilder) {
Token type = lexer.consume("Expected type before declaration", TokenType.TYPE_STRING, TokenType.TYPE_NUMBER, TokenType.TYPE_BOOLEAN, TokenType.TYPE_VOID);
Token identifier = lexer.consume("Expected identifier after type", TokenType.IDENTIFIER);
Returnable.ReturnType returnType = ParserUtil.getVariableReturnType(type); return switch(lexer.current().getType()) {
case ASSIGNMENT -> parseVariableDeclaration(scopeBuilder, type, identifier);
case OPEN_PAREN -> parseFunctionDeclaration(scopeBuilder, type, identifier);
default -> throw new ParseException("Expected '=' for variable assignment or '(' for function declaration after identifier '" + identifier.getContent() + "'", lexer.current().getPosition());
};
}
private Expression<?> parseVariableDeclaration(ScopeBuilder scopeBuilder, Token type, Token identifier) {
lexer.consume("Expected '=' after identifier '" + identifier.getContent() + "' for variable declaration", TokenType.ASSIGNMENT);
ParserUtil.checkVarType(type, returnType); // Check for type mismatch if (!type.isVariableDeclaration()) throw new ParseException("Expected type specification at beginning of variable declaration", type.getPosition());
Token identifier = tokens.consume();
ParserUtil.checkType(identifier, Token.Type.IDENTIFIER); if(scopeBuilder.containsVariable(identifier.getContent()))
if(functions.containsKey(identifier.getContent()) || scopeBuilder.contains(identifier.getContent()))
throw new ParseException(identifier.getContent() + " is already defined in this scope", identifier.getPosition()); 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); Expression<?> value = parseExpression(scopeBuilder);
ParserUtil.checkReturnType(value, returnType); ParserUtil.ensureReturnType(value, ParserUtil.getVariableReturnType(type));
String id = identifier.getContent(); String variableName = identifier.getContent();
return switch(value.returnType()) { return switch(value.returnType()) {
case NUMBER -> new NumAssignmentNode((Returnable<Number>) value, identifier.getPosition(), scopeBuilder.num(id)); case NUMBER -> new NumAssignmentNode((Expression<Number>) value, identifier.getPosition(),
case STRING -> new StrAssignmentNode((Returnable<String>) value, identifier.getPosition(), scopeBuilder.str(id)); scopeBuilder.declareNum(variableName));
case BOOLEAN -> new BoolAssignmentNode((Returnable<Boolean>) value, identifier.getPosition(), scopeBuilder.bool(id)); case STRING -> new StrAssignmentNode((Expression<String>) value, identifier.getPosition(),
scopeBuilder.declareStr(variableName));
case BOOLEAN -> new BoolAssignmentNode((Expression<Boolean>) value, identifier.getPosition(),
scopeBuilder.declareBool(variableName));
default -> throw new ParseException("Illegal type for variable declaration: " + type, value.getPosition()); default -> throw new ParseException("Illegal type for variable declaration: " + type, value.getPosition());
}; };
} }
private Block parseBlock(Tokenizer tokens, boolean loop, ScopeBuilder scopeBuilder) { private Expression<?> parseFunctionDeclaration(ScopeBuilder scopeBuilder, Token type, Token identifier) {
List<Item<?>> parsedItems = new ArrayList<>(); lexer.consume("Expected '(' after identifier '" + identifier.getContent() + "' for function declaration", TokenType.OPEN_PAREN);
scopeBuilder = scopeBuilder.sub(); if(!(type.isType(TokenType.TYPE_STRING, TokenType.TYPE_BOOLEAN, TokenType.TYPE_NUMBER, TokenType.TYPE_VOID)))
throw new ParseException("Invalid function declaration return type specification " + type.getType(), type.getPosition());
Token first = tokens.get(); if(scopeBuilder.containsVariable(identifier.getContent()))
throw new ParseException(identifier.getContent() + " is already defined in this scope", identifier.getPosition());
while(tokens.hasNext()) { ReturnType returnType = ParserUtil.getVariableReturnType(type);
Token token = tokens.get();
if(token.getType().equals(Token.Type.BLOCK_END)) break; // Stop parsing at block end. ScopeBuilder functionBodyScope = scopeBuilder.functionScope();
Item<?> parsedItem = parseItem(tokens, loop, scopeBuilder);
if(parsedItem != Function.NULL) { // Declare parameter names into function body scope
parsedItems.add(parsedItem); List<Pair<Integer, ReturnType>> parameterInfo = getFunctionParameterDeclaration().stream().map(
} arg -> Pair.of(switch(arg.getRight()) {
if(tokens.hasNext() && !token.isLoopLike()) ParserUtil.checkType(tokens.consume(), Token.Type.STATEMENT_END); case NUMBER -> functionBodyScope.declareNum(arg.getLeft());
case BOOLEAN -> functionBodyScope.declareBool(arg.getLeft());
case STRING -> functionBodyScope.declareStr(arg.getLeft());
default -> throw new IllegalArgumentException("Unsupported parameter type: " + arg.getRight());
}, arg.getRight())).toList();
Block body = parseStatementBlock(functionBodyScope, returnType);
FunctionBuilder<?> functionBuilder = new UserDefinedFunctionBuilder<>(returnType, parameterInfo, body, functionBodyScope);
scopeBuilder.registerFunction(identifier.getContent(), functionBuilder);
return Expression.NOOP;
}
private List<Pair<String, ReturnType>> getFunctionParameterDeclaration() {
List<Pair<String, ReturnType>> parameters = new ArrayList<>();
while(lexer.current().getType() != TokenType.CLOSE_PAREN) {
// Parse parameter type
Token typeToken = lexer.consume("Expected function parameter type declaration", TokenType.TYPE_BOOLEAN, TokenType.TYPE_STRING, TokenType.TYPE_NUMBER);
ReturnType type = ParserUtil.getVariableReturnType(typeToken);
// Parse parameter name
Token identifierToken = lexer.consume("Expected function parameter identifier", TokenType.IDENTIFIER);
String name = identifierToken.getContent();
parameters.add(Pair.of(name, type));
// Consume separator if present, trailing separators are allowed
if(lexer.current().isType(TokenType.SEPARATOR)) lexer.consumeUnchecked();
} }
return new Block(parsedItems, first.getPosition()); lexer.consume("Expected ')' after function parameter declaration", TokenType.CLOSE_PAREN);
return parameters;
} }
private Item<?> parseItem(Tokenizer tokens, boolean loop, ScopeBuilder scopeBuilder) { private Block parseBlock(ScopeBuilder scopeBuilder, ReturnType blockReturnType) {
Token token = tokens.get(); List<Expression<?>> expressions = new ArrayList<>();
if(loop) ParserUtil.checkType(token, Token.Type.IDENTIFIER, Token.Type.IF_STATEMENT, Token.Type.WHILE_LOOP, Token.Type.FOR_LOOP, scopeBuilder = scopeBuilder.innerScope(); // Create new inner scope for the block
Token.Type.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, SourcePosition startPosition = lexer.current().getPosition();
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) boolean hasReturn = false;
return parseLoopLike(tokens, loop, scopeBuilder);
} else if(token.isIdentifier()) { // Parse identifiers // Parse each statement
if(scopeBuilder.contains(token.getContent())) { // Assume variable assignment while(lexer.hasNext() && !lexer.current().isType(TokenType.BLOCK_END)) {
return parseAssignment(tokens, scopeBuilder); Expression<?> expression = parseStatement(scopeBuilder);
} else return parseFunction(tokens, true, scopeBuilder); if(expression != Expression.NOOP) {
} else if(token.isVariableDeclaration()) { expressions.add(expression);
}
return parseVariableDeclaration(tokens, scopeBuilder); if(expression instanceof ReturnKeyword returnKeyword) {
hasReturn = true;
} else if(token.getType().equals(Token.Type.RETURN)) return new ReturnKeyword(tokens.consume().getPosition()); if(returnKeyword.dataReturnType() != blockReturnType)
else if(token.getType().equals(Token.Type.BREAK)) return new BreakKeyword(tokens.consume().getPosition()); throw new ParseException(
else if(token.getType().equals(Token.Type.CONTINUE)) return new ContinueKeyword(tokens.consume().getPosition()); "Invalid return type, expected " + blockReturnType + ", found " + returnKeyword.dataReturnType(),
else if(token.getType().equals(Token.Type.FAIL)) return new FailKeyword(tokens.consume().getPosition()); expression.getPosition());
else throw new UnsupportedOperationException("Unexpected token " + token.getType() + ": " + token.getPosition()); }
}
if(blockReturnType != ReturnType.VOID && !hasReturn)
throw new ParseException("Block does not contain a return statement, must return type " + blockReturnType, startPosition);
return new Block(expressions, startPosition, blockReturnType);
} }
private VariableAssignmentNode<?> parseAssignment(Tokenizer tokens, ScopeBuilder scopeBuilder) { private Expression<?> parseStatement(ScopeBuilder scopeBuilder) {
Token identifier = tokens.consume(); Token token = lexer.current();
Expression<?> expression = switch(token.getType()) {
case FOR_LOOP -> parseForLoop(scopeBuilder);
case IF_STATEMENT -> parseIfStatement(scopeBuilder);
case WHILE_LOOP -> parseWhileLoop(scopeBuilder);
case IDENTIFIER -> {
if(scopeBuilder.containsVariable(token.getContent())) yield parseAssignment(scopeBuilder); // Assume variable assignment
else yield parseFunctionInvocation(lexer.consumeUnchecked(), scopeBuilder);
}
case TYPE_NUMBER, TYPE_STRING, TYPE_BOOLEAN, TYPE_VOID -> parseDeclaration(scopeBuilder);
case RETURN -> parseReturn(scopeBuilder);
case BREAK -> {
if (!scopeBuilder.isInLoop()) throw new ParseException("Break statements can only be defined inside loops", token.getPosition());
yield new BreakKeyword(lexer.consumeUnchecked().getPosition());
}
case CONTINUE -> {
if (!scopeBuilder.isInLoop()) throw new ParseException("Continue statements can only be defined inside loops", token.getPosition());
yield new ContinueKeyword(lexer.consumeUnchecked().getPosition());
}
case FAIL -> new FailKeyword(lexer.consumeUnchecked().getPosition());
case STATEMENT_END -> Expression.NOOP;
default -> throw new UnsupportedOperationException("Unexpected token " + token.getType() + ": " + token.getPosition());
};
if(!token.isControlStructure() && expression != Expression.NOOP) lexer.consume("Expected ';' at end of statement", TokenType.STATEMENT_END);
return expression;
}
private ReturnKeyword parseReturn(ScopeBuilder scopeBuilder) {
Token returnToken = lexer.consume("Expected 'return' keyword at beginning of return statement", TokenType.RETURN);
Expression<?> data = null;
if(!lexer.current().isType(TokenType.STATEMENT_END)) {
data = parseExpression(scopeBuilder);
}
return new ReturnKeyword(data, returnToken.getPosition());
}
private VariableAssignmentNode<?> parseAssignment(ScopeBuilder scopeBuilder) {
Token identifier = lexer.consume("Expected identifier at beginning of assignment", TokenType.IDENTIFIER);
ParserUtil.checkType(identifier, Token.Type.IDENTIFIER); lexer.consume("Expected '=' after identifier for variable assignment", TokenType.ASSIGNMENT);
ParserUtil.checkType(tokens.consume(), Token.Type.ASSIGNMENT); Expression<?> value = parseExpression(scopeBuilder);
Returnable<?> value = parseExpression(tokens, true, scopeBuilder);
String id = identifier.getContent(); String id = identifier.getContent();
ParserUtil.checkReturnType(value, scopeBuilder.getType(id)); ParserUtil.ensureReturnType(value, scopeBuilder.getVaraibleType(id));
ReturnType type = value.returnType(); ReturnType type = value.returnType();
return switch(type) { return switch(type) {
case NUMBER -> new NumAssignmentNode((Returnable<Number>) value, identifier.getPosition(), scopeBuilder.getIndex(id)); case NUMBER -> new NumAssignmentNode((Expression<Number>) value, identifier.getPosition(), scopeBuilder.getIndex(id));
case STRING -> new StrAssignmentNode((Returnable<String>) value, identifier.getPosition(), scopeBuilder.getIndex(id)); case STRING -> new StrAssignmentNode((Expression<String>) value, identifier.getPosition(), scopeBuilder.getIndex(id));
case BOOLEAN -> new BoolAssignmentNode((Returnable<Boolean>) value, identifier.getPosition(), scopeBuilder.getIndex(id)); case BOOLEAN -> new BoolAssignmentNode((Expression<Boolean>) value, identifier.getPosition(), scopeBuilder.getIndex(id));
default -> throw new ParseException("Illegal type for variable assignment: " + type, value.getPosition()); default -> throw new ParseException("Illegal type for variable assignment: " + type, value.getPosition());
}; };
} }
private Function<?> parseFunction(Tokenizer tokens, boolean fullStatement, ScopeBuilder scopeBuilder) { private Expression<?> parseFunctionInvocation(Token identifier, ScopeBuilder scopeBuilder) {
Token identifier = tokens.consume(); if(!scopeBuilder.containsFunction(identifier.getContent()))
ParserUtil.checkType(identifier, Token.Type.IDENTIFIER); // First token must be identifier throw new ParseException("Function '" + identifier.getContent() + "' is not defined in this scope", identifier.getPosition());
if(!functions.containsKey(identifier.getContent())) FunctionBuilder<?> builder = scopeBuilder.getFunction(identifier.getContent());
throw new ParseException("No such function \"" + identifier.getContent() + "\"", identifier.getPosition());
ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_BEGIN); // Second is body begin lexer.consume("Expected '(' after identifier " + identifier.getContent(), TokenType.OPEN_PAREN); // Invocation starts with open paren
List<Expression<?>> args = new ArrayList<>();
List<Returnable<?>> args = getArgs(tokens, scopeBuilder); // Extract arguments, consume the rest. while(!lexer.current().isType(TokenType.CLOSE_PAREN)) {
args.add(parseExpression(scopeBuilder));
ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_END); // Remove body end if (lexer.current().isType(TokenType.CLOSE_PAREN)) break;
lexer.consume("Expected ',' between function arguments", TokenType.SEPARATOR);
if(fullStatement) ParserUtil.checkType(tokens.get(), Token.Type.STATEMENT_END); }
lexer.consume("Expected ')' after function arguments", TokenType.CLOSE_PAREN);
if(ignoredFunctions.contains(identifier.getContent())) { if(ignoredFunctions.contains(identifier.getContent())) {
return Function.NULL; return Expression.NOOP;
} }
if(functions.containsKey(identifier.getContent())) { if(builder.argNumber() != -1 && args.size() != builder.argNumber())
FunctionBuilder<?> builder = functions.get(identifier.getContent()); throw new ParseException("Expected " + builder.argNumber() + " arguments, found " + args.size(), identifier.getPosition());
if(builder.argNumber() != -1 && args.size() != builder.argNumber()) for(int i = 0; i < args.size(); i++) {
throw new ParseException("Expected " + builder.argNumber() + " arguments, found " + args.size(), identifier.getPosition()); Expression<?> argument = args.get(i);
if(builder.getArgument(i) == null)
for(int i = 0; i < args.size(); i++) { throw new ParseException("Unexpected argument at position " + i + " in function " + identifier.getContent(),
Returnable<?> argument = args.get(i); identifier.getPosition());
if(builder.getArgument(i) == null) ParserUtil.ensureReturnType(argument, builder.getArgument(i));
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()); return builder.build(args, identifier.getPosition());
} }
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;
}
} }
@@ -12,104 +12,32 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.dfsek.terra.addons.terrascript.lexer.Token;
import com.dfsek.terra.addons.terrascript.lexer.Token.TokenType;
import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; 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.Expression;
import com.dfsek.terra.addons.terrascript.tokenizer.Token;
public class ParserUtil { public class ParserUtil {
private static final Map<Token.Type, Map<Token.Type, Boolean>> PRECEDENCE = new HashMap<>(); // If second has precedence, true. // public static void ensureType(Token token, TokenType... expected) {
private static final List<Token.Type> ARITHMETIC = Arrays.asList(Token.Type.ADDITION_OPERATOR, Token.Type.SUBTRACTION_OPERATOR, // for(TokenType type : expected) if(token.getType().equals(type)) return;
Token.Type.MULTIPLICATION_OPERATOR, Token.Type.DIVISION_OPERATOR, // throw new ParseException("Expected " + Arrays.toString(expected) + " but found " + token.getType(), token.getPosition());
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 public static void ensureReturnType(Expression<?> returnable, Expression.ReturnType... types) {
Map<Token.Type, Boolean> add = new HashMap<>(); // Addition/subtraction before Multiplication/division. for(Expression.ReturnType type : types) if(returnable.returnType().equals(type)) return;
add.put(Token.Type.MULTIPLICATION_OPERATOR, true); throw new ParseException("Invalid type " + returnable.returnType() + ", expected " + (types.length == 1 ? types[0].toString() : "one of " + Arrays.toString(types)), returnable.getPosition());
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) { public static Expression.ReturnType getVariableReturnType(Token varToken) {
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()) { return switch(varToken.getType()) {
case NUMBER_VARIABLE -> Returnable.ReturnType.NUMBER; case TYPE_NUMBER -> Expression.ReturnType.NUMBER;
case STRING_VARIABLE -> Returnable.ReturnType.STRING; case TYPE_STRING -> Expression.ReturnType.STRING;
case BOOLEAN_VARIABLE -> Returnable.ReturnType.BOOLEAN; case TYPE_BOOLEAN -> Expression.ReturnType.BOOLEAN;
case TYPE_VOID -> Expression.ReturnType.VOID;
default -> throw new ParseException("Unexpected token " + varToken.getType() + "; expected variable declaration", default -> throw new ParseException("Unexpected token " + varToken.getType() + "; expected variable declaration",
varToken.getPosition()); 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);
}
} }
@@ -9,30 +9,30 @@ package com.dfsek.terra.addons.terrascript.parser.exceptions;
import java.io.Serial; import java.io.Serial;
import com.dfsek.terra.addons.terrascript.tokenizer.Position; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
public class ParseException extends RuntimeException { public class ParseException extends RuntimeException {
@Serial @Serial
private static final long serialVersionUID = 6744390543046766386L; private static final long serialVersionUID = 6744390543046766386L;
private final Position position; private final SourcePosition position;
public ParseException(String message, Position position) { public ParseException(String message, SourcePosition position) {
super(message); super(message);
this.position = position; this.position = position;
} }
public ParseException(String message, Position position, Throwable cause) { public ParseException(String message, SourcePosition position, Throwable cause) {
super(message, cause); super(message, cause);
this.position = position; this.position = position;
} }
@Override @Override
public String getMessage() { public String getMessage() {
return super.getMessage() + ": " + position; return "Error at " + position + ": " + super.getMessage();
} }
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
} }
@@ -9,35 +9,43 @@ package com.dfsek.terra.addons.terrascript.parser.lang;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.terrascript.tokenizer.Position; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationInfo;
public class Block implements Item<Block.ReturnInfo<?>> { public class Block implements Expression<EvaluationInfo<?>> {
private final List<Item<?>> items; private final List<Expression<?>> items;
private final Position position; private final SourcePosition position;
private final ReturnType returnType;
public Block(List<Item<?>> items, Position position) { public Block(List<Expression<?>> items, SourcePosition position, ReturnType returnType) {
this.items = items; this.items = items;
this.position = position; this.position = position;
this.returnType = returnType;
} }
@Override @Override
public ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) { public ReturnType returnType() {
for(Item<?> item : items) { return returnType;
Object result = item.apply(implementationArguments, scope); }
if(result instanceof ReturnInfo<?> level) {
if(!level.getLevel().equals(ReturnLevel.NONE)) return level; @Override
public EvaluationInfo<?> evaluate(ImplementationArguments implementationArguments, Scope scope) {
for(Expression<?> item : items) {
Object result = item.evaluate(implementationArguments, scope);
if(result instanceof EvaluationInfo<?> evalInfo) {
if(!evalInfo.level().equals(EvaluationLevel.NONE)) return evalInfo;
} }
} }
return new ReturnInfo<>(ReturnLevel.NONE, null); return new EvaluationInfo<>(EvaluationLevel.NONE, Expression.NOOP);
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
public enum ReturnLevel { public enum EvaluationLevel {
NONE(false), NONE(false),
BREAK(false), BREAK(false),
CONTINUE(false), CONTINUE(false),
@@ -46,7 +54,7 @@ public class Block implements Item<Block.ReturnInfo<?>> {
private final boolean returnFast; private final boolean returnFast;
ReturnLevel(boolean returnFast) { EvaluationLevel(boolean returnFast) {
this.returnFast = returnFast; this.returnFast = returnFast;
} }
@@ -56,21 +64,6 @@ public class Block implements Item<Block.ReturnInfo<?>> {
} }
public static class ReturnInfo<T> { public record EvaluationInfo<T extends Expression<?>>(EvaluationLevel level, T data) {
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;
}
} }
} }
@@ -7,13 +7,13 @@ import com.dfsek.terra.addons.terrascript.parser.lang.Scope.ScopeBuilder;
public class Executable { public class Executable {
private final Block script; private final Block script;
private final ThreadLocal<Scope> scope; private final ThreadLocal<Scope> scope;
public Executable(Block script, ScopeBuilder scopeBuilder) { public Executable(Block script, ScopeBuilder scopeBuilder) {
this.script = script; this.script = script;
this.scope = ThreadLocal.withInitial(scopeBuilder::build); this.scope = ThreadLocal.withInitial(scopeBuilder::build);
} }
public boolean execute(ImplementationArguments arguments) { public boolean execute(ImplementationArguments arguments) {
return script.apply(arguments, scope.get()).getLevel() != Block.ReturnLevel.FAIL; return script.evaluate(arguments, scope.get()).level() != Block.EvaluationLevel.FAIL;
} }
} }
@@ -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.parser.lang;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
public interface Expression<T> {
Expression<Void> NOOP = new Expression<>() {
@Override
public ReturnType returnType() {
return ReturnType.VOID;
}
@Override
public Void evaluate(ImplementationArguments implementationArguments, Scope scope) {
return null;
}
@Override
public SourcePosition getPosition() {
return null;
}
};
ReturnType returnType();
T evaluate(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");
}
SourcePosition getPosition();
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;
}
}
}
@@ -8,7 +8,7 @@
package com.dfsek.terra.addons.terrascript.parser.lang; package com.dfsek.terra.addons.terrascript.parser.lang;
/** /**
* Arguments passed to {@link Item}s by the implementation * Arguments passed to {@link Expression}s by the implementation
*/ */
public interface ImplementationArguments { public interface ImplementationArguments {
} }
@@ -1,25 +0,0 @@
/*
* 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();
}
@@ -7,5 +7,5 @@
package com.dfsek.terra.addons.terrascript.parser.lang; package com.dfsek.terra.addons.terrascript.parser.lang;
public interface Keyword<T> extends Returnable<T> { public interface Keyword<T> extends Expression<T> {
} }
@@ -1,30 +0,0 @@
/*
* 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;
}
}
}
@@ -6,7 +6,9 @@ import net.jafama.FastMath;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable.ReturnType; import com.dfsek.terra.addons.terrascript.parser.lang.Expression.ReturnType;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder;
import com.dfsek.terra.api.util.generic.pair.Pair; import com.dfsek.terra.api.util.generic.pair.Pair;
@@ -46,28 +48,57 @@ public class Scope {
} }
public static final class ScopeBuilder { public static final class ScopeBuilder {
private final Map<String, FunctionBuilder<? extends Function<?>>> functions;
private final Map<String, Pair<Integer, ReturnType>> indices; private final Map<String, Pair<Integer, ReturnType>> indices;
private int numSize, boolSize, strSize = 0; private int numSize, boolSize, strSize = 0;
private ScopeBuilder parent; private ScopeBuilder parent;
private boolean inLoop;
public ScopeBuilder() { public ScopeBuilder() {
this.functions = new HashMap<>();
this.indices = new HashMap<>(); this.indices = new HashMap<>();
} }
private ScopeBuilder(ScopeBuilder parent) { private ScopeBuilder(ScopeBuilder parent, boolean inLoop) {
this.parent = parent; this.parent = parent;
this.numSize = parent.numSize; this.numSize = parent.numSize;
this.boolSize = parent.boolSize; this.boolSize = parent.boolSize;
this.strSize = parent.strSize; this.strSize = parent.strSize;
this.functions = new HashMap<>(parent.functions);
this.indices = new HashMap<>(parent.indices); this.indices = new HashMap<>(parent.indices);
this.inLoop = inLoop;
}
private ScopeBuilder(Map<String, FunctionBuilder<? extends Function<?>>> functions) {
this.functions = new HashMap<>(functions);
this.indices = new HashMap<>();
} }
public Scope build() { public Scope build() {
return new Scope(numSize, boolSize, strSize); return new Scope(numSize, boolSize, strSize);
} }
public ScopeBuilder sub() { public ScopeBuilder innerScope() {
return new ScopeBuilder(this); return new ScopeBuilder(this, inLoop);
}
public ScopeBuilder innerLoopScope() { return new ScopeBuilder(this, true); }
public ScopeBuilder functionScope() { return new ScopeBuilder(functions); }
public ScopeBuilder registerFunction(String name, FunctionBuilder<? extends Function<?>> functionBuilder) {
functions.put(name, functionBuilder);
return this;
}
public boolean containsFunction(String functionName) {
return functions.containsKey(functionName);
}
public FunctionBuilder<?> getFunction(String functionName) {
return functions.get(functionName);
} }
private String check(String id) { private String check(String id) {
@@ -76,8 +107,12 @@ public class Scope {
} }
return id; return id;
} }
public int num(String id) { public boolean isInLoop() {
return inLoop;
}
public int declareNum(String id) {
int num = numSize; int num = numSize;
indices.put(check(id), Pair.of(num, ReturnType.NUMBER)); indices.put(check(id), Pair.of(num, ReturnType.NUMBER));
numSize++; numSize++;
@@ -85,7 +120,7 @@ public class Scope {
return num; return num;
} }
public int str(String id) { public int declareStr(String id) {
int str = strSize; int str = strSize;
indices.put(check(id), Pair.of(str, ReturnType.STRING)); indices.put(check(id), Pair.of(str, ReturnType.STRING));
strSize++; strSize++;
@@ -93,7 +128,7 @@ public class Scope {
return str; return str;
} }
public int bool(String id) { public int declareBool(String id) {
int bool = boolSize; int bool = boolSize;
indices.put(check(id), Pair.of(bool, ReturnType.BOOLEAN)); indices.put(check(id), Pair.of(bool, ReturnType.BOOLEAN));
boolSize++; boolSize++;
@@ -107,14 +142,14 @@ public class Scope {
parent.updateBoolSize(size); parent.updateBoolSize(size);
} }
} }
private void updateNumSize(int size) { private void updateNumSize(int size) {
this.numSize = FastMath.max(numSize, size); this.numSize = FastMath.max(numSize, size);
if(parent != null) { if(parent != null) {
parent.updateNumSize(size); parent.updateNumSize(size);
} }
} }
private void updateStrSize(int size) { private void updateStrSize(int size) {
this.strSize = FastMath.max(strSize, size); this.strSize = FastMath.max(strSize, size);
if(parent != null) { if(parent != null) {
@@ -126,12 +161,11 @@ public class Scope {
return indices.get(id).getLeft(); return indices.get(id).getLeft();
} }
public ReturnType getType(String id) { public ReturnType getVaraibleType(String id) {
return indices.get(id).getRight(); return indices.get(id).getRight();
} }
public boolean containsVariable(String id) {
public boolean contains(String id) {
return indices.containsKey(id); return indices.containsKey(id);
} }
} }
@@ -1,11 +0,0 @@
/*
* 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> {
}
@@ -7,15 +7,15 @@
package com.dfsek.terra.addons.terrascript.parser.lang.constants; package com.dfsek.terra.addons.terrascript.parser.lang.constants;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BooleanConstant extends ConstantExpression<Boolean> { public class BooleanConstant extends ConstantExpression<Boolean> {
private final boolean constant; private final boolean constant;
public BooleanConstant(Boolean constant, Position position) { public BooleanConstant(Boolean constant, SourcePosition position) {
super(constant, position); super(constant, position);
this.constant = constant; this.constant = constant;
} }
@@ -7,28 +7,28 @@
package com.dfsek.terra.addons.terrascript.parser.lang.constants; package com.dfsek.terra.addons.terrascript.parser.lang.constants;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public abstract class ConstantExpression<T> implements Returnable<T> { public abstract class ConstantExpression<T> implements Expression<T> {
private final T constant; private final T constant;
private final Position position; private final SourcePosition position;
public ConstantExpression(T constant, Position position) { public ConstantExpression(T constant, SourcePosition position) {
this.constant = constant; this.constant = constant;
this.position = position; this.position = position;
} }
@Override @Override
public T apply(ImplementationArguments implementationArguments, Scope scope) { public T evaluate(ImplementationArguments implementationArguments, Scope scope) {
return constant; return constant;
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
@@ -7,16 +7,16 @@
package com.dfsek.terra.addons.terrascript.parser.lang.constants; package com.dfsek.terra.addons.terrascript.parser.lang.constants;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class NumericConstant extends ConstantExpression<Number> { public class NumericConstant extends ConstantExpression<Number> {
private final double constant; private final double constant;
public NumericConstant(Number constant, Position position) { public NumericConstant(Number constant, SourcePosition position) {
super(constant, position); super(constant, position);
this.constant = constant.doubleValue(); this.constant = constant.doubleValue();
} }
@@ -27,7 +27,7 @@ public class NumericConstant extends ConstantExpression<Number> {
} }
@Override @Override
public Returnable.ReturnType returnType() { public Expression.ReturnType returnType() {
return Returnable.ReturnType.NUMBER; return Expression.ReturnType.NUMBER;
} }
} }
@@ -7,17 +7,17 @@
package com.dfsek.terra.addons.terrascript.parser.lang.constants; package com.dfsek.terra.addons.terrascript.parser.lang.constants;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.tokenizer.Position; import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public class StringConstant extends ConstantExpression<String> { public class StringConstant extends ConstantExpression<String> {
public StringConstant(String constant, Position position) { public StringConstant(String constant, SourcePosition position) {
super(constant, position); super(constant, position);
} }
@Override @Override
public Returnable.ReturnType returnType() { public Expression.ReturnType returnType() {
return Returnable.ReturnType.STRING; return Expression.ReturnType.STRING;
} }
} }
@@ -7,37 +7,20 @@
package com.dfsek.terra.addons.terrascript.parser.lang.functions; package com.dfsek.terra.addons.terrascript.parser.lang.functions;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public interface Function<T> extends Returnable<T> { public interface Function<T> extends Expression<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 @Override
default double applyDouble(ImplementationArguments implementationArguments, Scope scope) { default double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return ((Number) apply(implementationArguments, scope)).doubleValue(); return ((Number) evaluate(implementationArguments, scope)).doubleValue();
} }
@Override @Override
default boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) { default boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
return (Boolean) apply(implementationArguments, scope); return (Boolean) evaluate(implementationArguments, scope);
} }
} }
@@ -9,14 +9,17 @@ package com.dfsek.terra.addons.terrascript.parser.lang.functions;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.tokenizer.Position; import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public interface FunctionBuilder<T extends Function<?>> { public interface FunctionBuilder<T extends Function<?>> {
T build(List<Returnable<?>> argumentList, Position position); T build(List<Expression<?>> argumentList, SourcePosition position);
/**
* @return Number of function arguments, -1 if the function uses a vararg at the end
*/
int argNumber(); int argNumber();
Returnable.ReturnType getArgument(int position); Expression.ReturnType getArgument(int position);
} }
@@ -0,0 +1,8 @@
package com.dfsek.terra.addons.terrascript.parser.lang.functions;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression.ReturnType;
import com.dfsek.terra.api.util.generic.pair.Pair;
public record FunctionSignature(ReturnType returnType, Pair<String, ReturnType>[] arguments) {
}
@@ -0,0 +1,74 @@
package com.dfsek.terra.addons.terrascript.parser.lang.functions;
import java.util.List;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Block;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression.ReturnType;
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.Scope.ScopeBuilder;
import com.dfsek.terra.api.util.generic.pair.Pair;
public class UserDefinedFunctionBuilder<T extends Function<?>> implements FunctionBuilder<T> {
private final ReturnType returnType;
private final List<Pair<Integer, ReturnType>> parameterInfo;
private final ScopeBuilder bodyScopeBuilder;
private final Block body;
public UserDefinedFunctionBuilder(ReturnType returnType, List<Pair<Integer, ReturnType>> parameterInfo, Block body,
ScopeBuilder functionBodyScope) {
this.returnType = returnType;
this.bodyScopeBuilder = functionBodyScope;
this.body = body;
this.parameterInfo = parameterInfo;
}
@Override
public T build(List<Expression<?>> argumentList, SourcePosition position) {
//noinspection unchecked
return (T) new Function() {
private final ThreadLocal<Scope> threadLocalScope = ThreadLocal.withInitial(bodyScopeBuilder::build);
@Override
public ReturnType returnType() {
return returnType;
}
@Override
public Object evaluate(ImplementationArguments implementationArguments, Scope scope) {
Scope bodyScope = threadLocalScope.get();
// Pass arguments into scope of function body
for(int i = 0; i < argumentList.size(); i++) {
Pair<Integer, ReturnType> paramInfo = parameterInfo.get(i);
Expression<?> argExpression = argumentList.get(i);
switch(paramInfo.getRight()) {
case NUMBER -> bodyScope.setNum(paramInfo.getLeft(), argExpression.applyDouble(implementationArguments, scope));
case BOOLEAN -> bodyScope.setBool(paramInfo.getLeft(), argExpression.applyBoolean(implementationArguments, scope));
case STRING -> bodyScope.setStr(paramInfo.getLeft(), (String) argExpression.evaluate(implementationArguments, scope));
}
}
return body.evaluate(implementationArguments, bodyScope).data().evaluate(implementationArguments, scope);
}
@Override
public SourcePosition getPosition() {
return position;
}
};
}
@Override
public int argNumber() {
return parameterInfo.size();
}
@Override
public ReturnType getArgument(int position) {
return parameterInfo.get(position).getRight();
}
}
@@ -7,27 +7,28 @@
package com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow; 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.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationInfo;
import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationLevel;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Keyword;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope; import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BreakKeyword implements Keyword<Block.ReturnInfo<?>> { public class BreakKeyword implements Keyword<EvaluationInfo<?>> {
private final Position position; private final SourcePosition position;
public BreakKeyword(Position position) { public BreakKeyword(SourcePosition position) {
this.position = position; this.position = position;
} }
@Override @Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) { public EvaluationInfo<?> evaluate(ImplementationArguments implementationArguments, Scope scope) {
return new Block.ReturnInfo<>(Block.ReturnLevel.BREAK, null); return new EvaluationInfo<>(EvaluationLevel.BREAK, null);
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
@@ -7,27 +7,28 @@
package com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow; 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.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationInfo;
import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationLevel;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Keyword;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope; import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ContinueKeyword implements Keyword<Block.ReturnInfo<?>> { public class ContinueKeyword implements Keyword<EvaluationInfo<?>> {
private final Position position; private final SourcePosition position;
public ContinueKeyword(Position position) { public ContinueKeyword(SourcePosition position) {
this.position = position; this.position = position;
} }
@Override @Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) { public EvaluationInfo<?> evaluate(ImplementationArguments implementationArguments, Scope scope) {
return new Block.ReturnInfo<>(Block.ReturnLevel.CONTINUE, null); return new EvaluationInfo<>(EvaluationLevel.CONTINUE, null);
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
@@ -7,27 +7,28 @@
package com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow; 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.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationInfo;
import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationLevel;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Keyword;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope; import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class FailKeyword implements Keyword<Block.ReturnInfo<?>> { public class FailKeyword implements Keyword<EvaluationInfo<?>> {
private final Position position; private final SourcePosition position;
public FailKeyword(Position position) { public FailKeyword(SourcePosition position) {
this.position = position; this.position = position;
} }
@Override @Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) { public EvaluationInfo<?> evaluate(ImplementationArguments implementationArguments, Scope scope) {
return new Block.ReturnInfo<>(Block.ReturnLevel.FAIL, null); return new EvaluationInfo<>(EvaluationLevel.FAIL, null);
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
@@ -7,27 +7,34 @@
package com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow; package com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow;
import com.dfsek.terra.addons.terrascript.parser.lang.Block; import javax.annotation.Nullable;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationInfo;
import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationLevel;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Keyword;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope; import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ReturnKeyword implements Keyword<Block.ReturnInfo<?>> { public class ReturnKeyword implements Keyword<EvaluationInfo<?>> {
private final Position position; private final SourcePosition position;
public ReturnKeyword(Position position) { private final Expression<?> data;
public ReturnKeyword(@Nullable Expression<?> data, SourcePosition position) {
this.data = data;
this.position = position; this.position = position;
} }
@Override @Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) { public EvaluationInfo<?> evaluate(ImplementationArguments implementationArguments, Scope scope) {
return new Block.ReturnInfo<>(Block.ReturnLevel.RETURN, null); return new EvaluationInfo<>(EvaluationLevel.RETURN, data);
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
@@ -35,4 +42,12 @@ public class ReturnKeyword implements Keyword<Block.ReturnInfo<?>> {
public ReturnType returnType() { public ReturnType returnType() {
return ReturnType.VOID; return ReturnType.VOID;
} }
public ReturnType dataReturnType() {
if(data != null) {
return data.returnType();
} else {
return ReturnType.VOID;
}
}
} }
@@ -7,23 +7,24 @@
package com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike; package com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Block; import com.dfsek.terra.addons.terrascript.parser.lang.Block;
import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationLevel;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.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.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ForKeyword implements Keyword<Block.ReturnInfo<?>> { public class ForKeyword implements Keyword<Block.EvaluationInfo<?>> {
private final Block conditional; private final Block conditional;
private final Item<?> initializer; private final Expression<?> initializer;
private final Returnable<Boolean> statement; private final Expression<Boolean> statement;
private final Item<?> incrementer; private final Expression<?> incrementer;
private final Position position; private final SourcePosition position;
public ForKeyword(Block conditional, Item<?> initializer, Returnable<Boolean> statement, Item<?> incrementer, Position position) { public ForKeyword(Block conditional, Expression<?> initializer, Expression<Boolean> statement, Expression<?> incrementer,
SourcePosition position) {
this.conditional = conditional; this.conditional = conditional;
this.initializer = initializer; this.initializer = initializer;
this.statement = statement; this.statement = statement;
@@ -32,19 +33,19 @@ public class ForKeyword implements Keyword<Block.ReturnInfo<?>> {
} }
@Override @Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) { public Block.EvaluationInfo<?> evaluate(ImplementationArguments implementationArguments, Scope scope) {
for(initializer.apply(implementationArguments, scope); for(initializer.evaluate(implementationArguments, scope);
statement.apply(implementationArguments, scope); statement.evaluate(implementationArguments, scope);
incrementer.apply(implementationArguments, scope)) { incrementer.evaluate(implementationArguments, scope)) {
Block.ReturnInfo<?> level = conditional.apply(implementationArguments, scope); Block.EvaluationInfo<?> level = conditional.evaluate(implementationArguments, scope);
if(level.getLevel().equals(Block.ReturnLevel.BREAK)) break; if(level.level().equals(EvaluationLevel.BREAK)) break;
if(level.getLevel().isReturnFast()) return level; if(level.level().isReturnFast()) return level;
} }
return new Block.ReturnInfo<>(Block.ReturnLevel.NONE, null); return new Block.EvaluationInfo<>(EvaluationLevel.NONE, null);
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
@@ -11,24 +11,25 @@ import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Block; import com.dfsek.terra.addons.terrascript.parser.lang.Block;
import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationLevel;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.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.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.util.generic.pair.Pair; import com.dfsek.terra.api.util.generic.pair.Pair;
public class IfKeyword implements Keyword<Block.ReturnInfo<?>> { public class IfKeyword implements Keyword<Block.EvaluationInfo<?>> {
private final Block conditional; private final Block conditional;
private final Returnable<Boolean> statement; private final Expression<Boolean> statement;
private final Position position; private final SourcePosition position;
private final List<Pair<Returnable<Boolean>, Block>> elseIf; private final List<Pair<Expression<Boolean>, Block>> elseIf;
private final Block elseBlock; private final Block elseBlock;
public IfKeyword(Block conditional, Returnable<Boolean> statement, List<Pair<Returnable<Boolean>, Block>> elseIf, public IfKeyword(Block conditional, Expression<Boolean> statement, List<Pair<Expression<Boolean>, Block>> elseIf,
@Nullable Block elseBlock, Position position) { @Nullable Block elseBlock, SourcePosition position) {
this.conditional = conditional; this.conditional = conditional;
this.statement = statement; this.statement = statement;
this.position = position; this.position = position;
@@ -37,21 +38,21 @@ public class IfKeyword implements Keyword<Block.ReturnInfo<?>> {
} }
@Override @Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) { public Block.EvaluationInfo<?> evaluate(ImplementationArguments implementationArguments, Scope scope) {
if(statement.apply(implementationArguments, scope)) return conditional.apply(implementationArguments, scope); if(statement.evaluate(implementationArguments, scope)) return conditional.evaluate(implementationArguments, scope);
else { else {
for(Pair<Returnable<Boolean>, Block> pair : elseIf) { for(Pair<Expression<Boolean>, Block> pair : elseIf) {
if(pair.getLeft().apply(implementationArguments, scope)) { if(pair.getLeft().evaluate(implementationArguments, scope)) {
return pair.getRight().apply(implementationArguments, scope); return pair.getRight().evaluate(implementationArguments, scope);
} }
} }
if(elseBlock != null) return elseBlock.apply(implementationArguments, scope); if(elseBlock != null) return elseBlock.evaluate(implementationArguments, scope);
} }
return new Block.ReturnInfo<>(Block.ReturnLevel.NONE, null); return new Block.EvaluationInfo<>(EvaluationLevel.NONE, null);
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
@@ -7,37 +7,39 @@
package com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike; package com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Block; import com.dfsek.terra.addons.terrascript.parser.lang.Block;
import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationInfo;
import com.dfsek.terra.addons.terrascript.parser.lang.Block.EvaluationLevel;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.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.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class WhileKeyword implements Keyword<Block.ReturnInfo<?>> { public class WhileKeyword implements Keyword<EvaluationInfo<?>> {
private final Block conditional; private final Block conditional;
private final Returnable<Boolean> statement; private final Expression<Boolean> statement;
private final Position position; private final SourcePosition position;
public WhileKeyword(Block conditional, Returnable<Boolean> statement, Position position) { public WhileKeyword(Block conditional, Expression<Boolean> statement, SourcePosition position) {
this.conditional = conditional; this.conditional = conditional;
this.statement = statement; this.statement = statement;
this.position = position; this.position = position;
} }
@Override @Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) { public EvaluationInfo<?> evaluate(ImplementationArguments implementationArguments, Scope scope) {
while(statement.apply(implementationArguments, scope)) { while(statement.evaluate(implementationArguments, scope)) {
Block.ReturnInfo<?> level = conditional.apply(implementationArguments, scope); EvaluationInfo<?> level = conditional.evaluate(implementationArguments, scope);
if(level.getLevel().equals(Block.ReturnLevel.BREAK)) break; if(level.level().equals(EvaluationLevel.BREAK)) break;
if(level.getLevel().isReturnFast()) return level; if(level.level().isReturnFast()) return level;
} }
return new Block.ReturnInfo<>(Block.ReturnLevel.NONE, null); return new EvaluationInfo<>(EvaluationLevel.NONE, null);
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
@@ -7,23 +7,23 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations; package com.dfsek.terra.addons.terrascript.parser.lang.operations;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.tokenizer.Position; import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public abstract class BinaryOperation<I, O> implements Returnable<O> { public abstract class BinaryOperation<I, O> implements Expression<O> {
protected final Returnable<I> left; protected final Expression<I> left;
protected final Returnable<I> right; protected final Expression<I> right;
private final Position start; private final SourcePosition start;
public BinaryOperation(Returnable<I> left, Returnable<I> right, Position start) { public BinaryOperation(Expression<I> left, Expression<I> right, SourcePosition start) {
this.left = left; this.left = left;
this.right = right; this.right = right;
this.start = start; this.start = start;
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return start; return start;
} }
} }
@@ -7,14 +7,14 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations; package com.dfsek.terra.addons.terrascript.parser.lang.operations;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BooleanAndOperation extends BinaryOperation<Boolean, Boolean> { public class BooleanAndOperation extends BinaryOperation<Boolean, Boolean> {
public BooleanAndOperation(Returnable<Boolean> left, Returnable<Boolean> right, Position start) { public BooleanAndOperation(Expression<Boolean> left, Expression<Boolean> right, SourcePosition start) {
super(left, right, start); super(left, right, start);
} }
@@ -24,7 +24,7 @@ public class BooleanAndOperation extends BinaryOperation<Boolean, Boolean> {
} }
@Override @Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) { public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope); return applyBoolean(implementationArguments, scope);
} }
@@ -7,19 +7,19 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations; package com.dfsek.terra.addons.terrascript.parser.lang.operations;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BooleanNotOperation extends UnaryOperation<Boolean> { public class BooleanNotOperation extends UnaryOperation<Boolean> {
public BooleanNotOperation(Returnable<Boolean> input, Position position) { public BooleanNotOperation(Expression<Boolean> input, SourcePosition position) {
super(input, position); super(input, position);
} }
@Override @Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) { public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope); return applyBoolean(implementationArguments, scope);
} }
@@ -7,19 +7,19 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations; package com.dfsek.terra.addons.terrascript.parser.lang.operations;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BooleanOrOperation extends BinaryOperation<Boolean, Boolean> { public class BooleanOrOperation extends BinaryOperation<Boolean, Boolean> {
public BooleanOrOperation(Returnable<Boolean> left, Returnable<Boolean> right, Position start) { public BooleanOrOperation(Expression<Boolean> left, Expression<Boolean> right, SourcePosition start) {
super(left, right, start); super(left, right, start);
} }
@Override @Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) { public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope); return applyBoolean(implementationArguments, scope);
} }
@@ -27,7 +27,7 @@ public class BooleanOrOperation extends BinaryOperation<Boolean, Boolean> {
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) { public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
return left.applyBoolean(implementationArguments, scope) || right.applyBoolean(implementationArguments, scope); return left.applyBoolean(implementationArguments, scope) || right.applyBoolean(implementationArguments, scope);
} }
@Override @Override
public ReturnType returnType() { public ReturnType returnType() {
return ReturnType.BOOLEAN; return ReturnType.BOOLEAN;
@@ -7,14 +7,14 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations; package com.dfsek.terra.addons.terrascript.parser.lang.operations;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ConcatenationOperation extends BinaryOperation<Object, Object> { public class ConcatenationOperation extends BinaryOperation<Object, Object> {
public ConcatenationOperation(Returnable<Object> left, Returnable<Object> right, Position position) { public ConcatenationOperation(Expression<Object> left, Expression<Object> right, SourcePosition position) {
super(left, right, position); super(left, right, position);
} }
@@ -30,12 +30,12 @@ public class ConcatenationOperation extends BinaryOperation<Object, Object> {
} }
@Override @Override
public Returnable.ReturnType returnType() { public Expression.ReturnType returnType() {
return Returnable.ReturnType.STRING; return Expression.ReturnType.STRING;
} }
@Override @Override
public Object apply(ImplementationArguments implementationArguments, Scope scope) { public Object evaluate(ImplementationArguments implementationArguments, Scope scope) {
return toString(left.apply(implementationArguments, scope)) + toString(right.apply(implementationArguments, scope)); return toString(left.evaluate(implementationArguments, scope)) + toString(right.evaluate(implementationArguments, scope));
} }
} }
@@ -7,24 +7,24 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations; package com.dfsek.terra.addons.terrascript.parser.lang.operations;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class DivisionOperation extends BinaryOperation<Number, Number> { public class DivisionOperation extends BinaryOperation<Number, Number> {
public DivisionOperation(Returnable<Number> left, Returnable<Number> right, Position position) { public DivisionOperation(Expression<Number> left, Expression<Number> right, SourcePosition position) {
super(left, right, position); super(left, right, position);
} }
@Override @Override
public Returnable.ReturnType returnType() { public Expression.ReturnType returnType() {
return Returnable.ReturnType.NUMBER; return Expression.ReturnType.NUMBER;
} }
@Override @Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) { public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope); return applyDouble(implementationArguments, scope);
} }
@@ -7,19 +7,19 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations; package com.dfsek.terra.addons.terrascript.parser.lang.operations;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ModuloOperation extends BinaryOperation<Number, Number> { public class ModuloOperation extends BinaryOperation<Number, Number> {
public ModuloOperation(Returnable<Number> left, Returnable<Number> right, Position start) { public ModuloOperation(Expression<Number> left, Expression<Number> right, SourcePosition start) {
super(left, right, start); super(left, right, start);
} }
@Override @Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) { public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope); return applyDouble(implementationArguments, scope);
} }
@@ -27,7 +27,7 @@ public class ModuloOperation extends BinaryOperation<Number, Number> {
public double applyDouble(ImplementationArguments implementationArguments, Scope scope) { public double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) % right.applyDouble(implementationArguments, scope); return left.applyDouble(implementationArguments, scope) % right.applyDouble(implementationArguments, scope);
} }
@Override @Override
public ReturnType returnType() { public ReturnType returnType() {
return ReturnType.NUMBER; return ReturnType.NUMBER;
@@ -7,19 +7,19 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations; package com.dfsek.terra.addons.terrascript.parser.lang.operations;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class MultiplicationOperation extends BinaryOperation<Number, Number> { public class MultiplicationOperation extends BinaryOperation<Number, Number> {
public MultiplicationOperation(Returnable<Number> left, Returnable<Number> right, Position position) { public MultiplicationOperation(Expression<Number> left, Expression<Number> right, SourcePosition position) {
super(left, right, position); super(left, right, position);
} }
@Override @Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) { public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope); return applyDouble(implementationArguments, scope);
} }
@@ -27,7 +27,7 @@ public class MultiplicationOperation extends BinaryOperation<Number, Number> {
public double applyDouble(ImplementationArguments implementationArguments, Scope scope) { public double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) * right.applyDouble(implementationArguments, scope); return left.applyDouble(implementationArguments, scope) * right.applyDouble(implementationArguments, scope);
} }
@Override @Override
public ReturnType returnType() { public ReturnType returnType() {
return ReturnType.NUMBER; return ReturnType.NUMBER;
@@ -7,14 +7,14 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations; package com.dfsek.terra.addons.terrascript.parser.lang.operations;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class NegationOperation extends UnaryOperation<Number> { public class NegationOperation extends UnaryOperation<Number> {
public NegationOperation(Returnable<Number> input, Position position) { public NegationOperation(Expression<Number> input, SourcePosition position) {
super(input, position); super(input, position);
} }
@@ -24,7 +24,7 @@ public class NegationOperation extends UnaryOperation<Number> {
} }
@Override @Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) { public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope); return applyDouble(implementationArguments, scope);
} }
@@ -7,19 +7,19 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations; package com.dfsek.terra.addons.terrascript.parser.lang.operations;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class NumberAdditionOperation extends BinaryOperation<Number, Number> { public class NumberAdditionOperation extends BinaryOperation<Number, Number> {
public NumberAdditionOperation(Returnable<Number> left, Returnable<Number> right, Position position) { public NumberAdditionOperation(Expression<Number> left, Expression<Number> right, SourcePosition position) {
super(left, right, position); super(left, right, position);
} }
@Override @Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) { public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope); return applyDouble(implementationArguments, scope);
} }
@@ -27,7 +27,7 @@ public class NumberAdditionOperation extends BinaryOperation<Number, Number> {
public double applyDouble(ImplementationArguments implementationArguments, Scope scope) { public double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) + right.applyDouble(implementationArguments, scope); return left.applyDouble(implementationArguments, scope) + right.applyDouble(implementationArguments, scope);
} }
@Override @Override
public ReturnType returnType() { public ReturnType returnType() {
return ReturnType.NUMBER; return ReturnType.NUMBER;
@@ -7,19 +7,19 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations; package com.dfsek.terra.addons.terrascript.parser.lang.operations;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class SubtractionOperation extends BinaryOperation<Number, Number> { public class SubtractionOperation extends BinaryOperation<Number, Number> {
public SubtractionOperation(Returnable<Number> left, Returnable<Number> right, Position position) { public SubtractionOperation(Expression<Number> left, Expression<Number> right, SourcePosition position) {
super(left, right, position); super(left, right, position);
} }
@Override @Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) { public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope); return applyDouble(implementationArguments, scope);
} }
@@ -27,7 +27,7 @@ public class SubtractionOperation extends BinaryOperation<Number, Number> {
public double applyDouble(ImplementationArguments implementationArguments, Scope scope) { public double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) - right.applyDouble(implementationArguments, scope); return left.applyDouble(implementationArguments, scope) - right.applyDouble(implementationArguments, scope);
} }
@Override @Override
public ReturnType returnType() { public ReturnType returnType() {
return ReturnType.NUMBER; return ReturnType.NUMBER;
@@ -7,21 +7,21 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations; package com.dfsek.terra.addons.terrascript.parser.lang.operations;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.tokenizer.Position; import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public abstract class UnaryOperation<T> implements Returnable<T> { public abstract class UnaryOperation<T> implements Expression<T> {
protected final Returnable<T> input; protected final Expression<T> input;
private final Position position; private final SourcePosition position;
public UnaryOperation(Returnable<T> input, Position position) { public UnaryOperation(Expression<T> input, SourcePosition position) {
this.input = input; this.input = input;
this.position = position; this.position = position;
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
} }
@@ -9,40 +9,40 @@ package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements;
import net.jafama.FastMath; import net.jafama.FastMath;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation; 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; import static com.dfsek.terra.api.util.MathUtil.EPSILON;
public class EqualsStatement extends BinaryOperation<Object, Boolean> { public class EqualsStatement extends BinaryOperation<Object, Boolean> {
public EqualsStatement(Returnable<Object> left, Returnable<Object> right, Position position) { public EqualsStatement(Expression<Object> left, Expression<Object> right, SourcePosition position) {
super(left, right, position); super(left, right, position);
} }
@Override @Override
public Returnable.ReturnType returnType() { public Expression.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN; return Expression.ReturnType.BOOLEAN;
} }
@Override @Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) { public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope); return applyBoolean(implementationArguments, scope);
} }
@Override @Override
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) { public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
Object leftValue = left.apply(implementationArguments, scope); Object leftValue = left.evaluate(implementationArguments, scope);
Object rightValue = right.apply(implementationArguments, scope); Object rightValue = right.evaluate(implementationArguments, scope);
if(leftValue instanceof Number l && rightValue instanceof Number r) { if(leftValue instanceof Number l && rightValue instanceof Number r) {
return FastMath.abs(l.doubleValue() - r.doubleValue()) <= EPSILON; return FastMath.abs(l.doubleValue() - r.doubleValue()) <= EPSILON;
} }
return leftValue.equals(rightValue); return leftValue.equals(rightValue);
} }
} }
@@ -7,25 +7,25 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements; package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation; 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 class GreaterOrEqualsThanStatement extends BinaryOperation<Number, Boolean> {
public GreaterOrEqualsThanStatement(Returnable<Number> left, Returnable<Number> right, Position position) { public GreaterOrEqualsThanStatement(Expression<Number> left, Expression<Number> right, SourcePosition position) {
super(left, right, position); super(left, right, position);
} }
@Override @Override
public Returnable.ReturnType returnType() { public Expression.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN; return Expression.ReturnType.BOOLEAN;
} }
@Override @Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) { public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope); return applyBoolean(implementationArguments, scope);
} }
@@ -7,21 +7,21 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements; package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation; 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 class GreaterThanStatement extends BinaryOperation<Number, Boolean> {
public GreaterThanStatement(Returnable<Number> left, Returnable<Number> right, Position position) { public GreaterThanStatement(Expression<Number> left, Expression<Number> right, SourcePosition position) {
super(left, right, position); super(left, right, position);
} }
@Override @Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) { public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope); return applyBoolean(implementationArguments, scope);
} }
@@ -31,7 +31,7 @@ public class GreaterThanStatement extends BinaryOperation<Number, Boolean> {
} }
@Override @Override
public Returnable.ReturnType returnType() { public Expression.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN; return Expression.ReturnType.BOOLEAN;
} }
} }
@@ -7,21 +7,21 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements; package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation; 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 class LessThanOrEqualsStatement extends BinaryOperation<Number, Boolean> {
public LessThanOrEqualsStatement(Returnable<Number> left, Returnable<Number> right, Position position) { public LessThanOrEqualsStatement(Expression<Number> left, Expression<Number> right, SourcePosition position) {
super(left, right, position); super(left, right, position);
} }
@Override @Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) { public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope); return applyBoolean(implementationArguments, scope);
} }
@@ -29,9 +29,9 @@ public class LessThanOrEqualsStatement extends BinaryOperation<Number, Boolean>
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) { public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) <= right.applyDouble(implementationArguments, scope); return left.applyDouble(implementationArguments, scope) <= right.applyDouble(implementationArguments, scope);
} }
@Override @Override
public Returnable.ReturnType returnType() { public Expression.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN; return Expression.ReturnType.BOOLEAN;
} }
} }
@@ -7,21 +7,21 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements; package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation; 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 class LessThanStatement extends BinaryOperation<Number, Boolean> {
public LessThanStatement(Returnable<Number> left, Returnable<Number> right, Position position) { public LessThanStatement(Expression<Number> left, Expression<Number> right, SourcePosition position) {
super(left, right, position); super(left, right, position);
} }
@Override @Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) { public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope); return applyBoolean(implementationArguments, scope);
} }
@@ -29,9 +29,9 @@ public class LessThanStatement extends BinaryOperation<Number, Boolean> {
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) { public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) < right.applyDouble(implementationArguments, scope); return left.applyDouble(implementationArguments, scope) < right.applyDouble(implementationArguments, scope);
} }
@Override @Override
public Returnable.ReturnType returnType() { public Expression.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN; return Expression.ReturnType.BOOLEAN;
} }
} }
@@ -9,29 +9,29 @@ package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements;
import net.jafama.FastMath; import net.jafama.FastMath;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation; 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; import static com.dfsek.terra.api.util.MathUtil.EPSILON;
public class NotEqualsStatement extends BinaryOperation<Object, Boolean> { public class NotEqualsStatement extends BinaryOperation<Object, Boolean> {
public NotEqualsStatement(Returnable<Object> left, Returnable<Object> right, Position position) { public NotEqualsStatement(Expression<Object> left, Expression<Object> right, SourcePosition position) {
super(left, right, position); super(left, right, position);
} }
@Override @Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) { public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope); return applyBoolean(implementationArguments, scope);
} }
@Override @Override
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) { public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
Object leftValue = left.apply(implementationArguments, scope); Object leftValue = left.evaluate(implementationArguments, scope);
Object rightValue = right.apply(implementationArguments, scope); Object rightValue = right.evaluate(implementationArguments, scope);
if(leftValue instanceof Number l && rightValue instanceof Number r) { if(leftValue instanceof Number l && rightValue instanceof Number r) {
return FastMath.abs(l.doubleValue() - r.doubleValue()) > EPSILON; return FastMath.abs(l.doubleValue() - r.doubleValue()) > EPSILON;
} }
@@ -40,7 +40,7 @@ public class NotEqualsStatement extends BinaryOperation<Object, Boolean> {
} }
@Override @Override
public Returnable.ReturnType returnType() { public Expression.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN; return Expression.ReturnType.BOOLEAN;
} }
} }
@@ -7,15 +7,15 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables; package com.dfsek.terra.addons.terrascript.parser.lang.variables;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.tokenizer.Position; import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public class BooleanVariable implements Variable<Boolean> { public class BooleanVariable implements Variable<Boolean> {
private final Position position; private final SourcePosition position;
private Boolean value; private Boolean value;
public BooleanVariable(Boolean value, Position position) { public BooleanVariable(Boolean value, SourcePosition position) {
this.value = value; this.value = value;
this.position = position; this.position = position;
} }
@@ -31,12 +31,12 @@ public class BooleanVariable implements Variable<Boolean> {
} }
@Override @Override
public Returnable.ReturnType getType() { public Expression.ReturnType getType() {
return Returnable.ReturnType.BOOLEAN; return Expression.ReturnType.BOOLEAN;
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
} }
@@ -7,15 +7,15 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables; package com.dfsek.terra.addons.terrascript.parser.lang.variables;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.tokenizer.Position; import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public class NumberVariable implements Variable<Number> { public class NumberVariable implements Variable<Number> {
private final Position position; private final SourcePosition position;
private Number value; private Number value;
public NumberVariable(Number value, Position position) { public NumberVariable(Number value, SourcePosition position) {
this.value = value; this.value = value;
this.position = position; this.position = position;
} }
@@ -31,12 +31,12 @@ public class NumberVariable implements Variable<Number> {
} }
@Override @Override
public Returnable.ReturnType getType() { public Expression.ReturnType getType() {
return Returnable.ReturnType.NUMBER; return Expression.ReturnType.NUMBER;
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
} }
@@ -7,15 +7,15 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables; package com.dfsek.terra.addons.terrascript.parser.lang.variables;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.tokenizer.Position; import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public class StringVariable implements Variable<String> { public class StringVariable implements Variable<String> {
private final Position position; private final SourcePosition position;
private String value; private String value;
public StringVariable(String value, Position position) { public StringVariable(String value, SourcePosition position) {
this.value = value; this.value = value;
this.position = position; this.position = position;
} }
@@ -31,12 +31,12 @@ public class StringVariable implements Variable<String> {
} }
@Override @Override
public Returnable.ReturnType getType() { public Expression.ReturnType getType() {
return Returnable.ReturnType.STRING; return Expression.ReturnType.STRING;
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
} }
@@ -7,8 +7,8 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables; package com.dfsek.terra.addons.terrascript.parser.lang.variables;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.tokenizer.Position; import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public interface Variable<T> { public interface Variable<T> {
@@ -16,7 +16,7 @@ public interface Variable<T> {
void setValue(T value); void setValue(T value);
Returnable.ReturnType getType(); Expression.ReturnType getType();
Position getPosition(); SourcePosition getPosition();
} }
@@ -1,18 +1,23 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables.assign; package com.dfsek.terra.addons.terrascript.parser.lang.variables.assign;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BoolAssignmentNode extends VariableAssignmentNode<Boolean> { public class BoolAssignmentNode extends VariableAssignmentNode<Boolean> {
public BoolAssignmentNode(Returnable<Boolean> value, Position position, int index) { public BoolAssignmentNode(Expression<Boolean> value, SourcePosition position, int index) {
super(value, position, index); super(value, position, index);
} }
@Override @Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) { public ReturnType returnType() {
return ReturnType.BOOLEAN;
}
@Override
public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope); return applyBoolean(implementationArguments, scope);
} }
@@ -1,18 +1,23 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables.assign; package com.dfsek.terra.addons.terrascript.parser.lang.variables.assign;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class NumAssignmentNode extends VariableAssignmentNode<Number> { public class NumAssignmentNode extends VariableAssignmentNode<Number> {
public NumAssignmentNode(Returnable<Number> value, Position position, int index) { public NumAssignmentNode(Expression<Number> value, SourcePosition position, int index) {
super(value, position, index); super(value, position, index);
} }
@Override @Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) { public ReturnType returnType() {
return ReturnType.NUMBER;
}
@Override
public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope); return applyDouble(implementationArguments, scope);
} }
@@ -1,19 +1,24 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables.assign; package com.dfsek.terra.addons.terrascript.parser.lang.variables.assign;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class StrAssignmentNode extends VariableAssignmentNode<String> { public class StrAssignmentNode extends VariableAssignmentNode<String> {
public StrAssignmentNode(Returnable<String> value, Position position, int index) { public StrAssignmentNode(Expression<String> value, SourcePosition position, int index) {
super(value, position, index); super(value, position, index);
} }
@Override @Override
public String apply(ImplementationArguments implementationArguments, Scope scope) { public ReturnType returnType() {
String val = value.apply(implementationArguments, scope); return ReturnType.STRING;
}
@Override
public String evaluate(ImplementationArguments implementationArguments, Scope scope) {
String val = value.evaluate(implementationArguments, scope);
scope.setStr(index, val); scope.setStr(index, val);
return val; return val;
} }
@@ -7,25 +7,24 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables.assign; 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.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public abstract class VariableAssignmentNode<T> implements Item<T> { public abstract class VariableAssignmentNode<T> implements Expression<T> {
protected final Returnable<T> value; protected final Expression<T> value;
protected final int index; protected final int index;
private final Position position; private final SourcePosition position;
public VariableAssignmentNode(Returnable<T> value, Position position, int index) { public VariableAssignmentNode(Expression<T> value, SourcePosition position, int index) {
this.value = value; this.value = value;
this.index = index; this.index = index;
this.position = position; this.position = position;
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
} }
@@ -1,17 +1,17 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables.reference; package com.dfsek.terra.addons.terrascript.parser.lang.variables.reference;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BoolVariableReferenceNode extends VariableReferenceNode<Boolean> { public class BoolVariableReferenceNode extends VariableReferenceNode<Boolean> {
public BoolVariableReferenceNode(Position position, ReturnType type, int index) { public BoolVariableReferenceNode(SourcePosition position, ReturnType type, int index) {
super(position, type, index); super(position, type, index);
} }
@Override @Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) { public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return scope.getBool(index); return scope.getBool(index);
} }
@@ -1,17 +1,17 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables.reference; package com.dfsek.terra.addons.terrascript.parser.lang.variables.reference;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class NumVariableReferenceNode extends VariableReferenceNode<Number> { public class NumVariableReferenceNode extends VariableReferenceNode<Number> {
public NumVariableReferenceNode(Position position, ReturnType type, int index) { public NumVariableReferenceNode(SourcePosition position, ReturnType type, int index) {
super(position, type, index); super(position, type, index);
} }
@Override @Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) { public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return scope.getNum(index); return scope.getNum(index);
} }
@@ -1,17 +1,17 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables.reference; package com.dfsek.terra.addons.terrascript.parser.lang.variables.reference;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class StrVariableReferenceNode extends VariableReferenceNode<String> { public class StrVariableReferenceNode extends VariableReferenceNode<String> {
public StrVariableReferenceNode(Position position, ReturnType type, int index) { public StrVariableReferenceNode(SourcePosition position, ReturnType type, int index) {
super(position, type, index); super(position, type, index);
} }
@Override @Override
public String apply(ImplementationArguments implementationArguments, Scope scope) { public String evaluate(ImplementationArguments implementationArguments, Scope scope) {
return scope.getStr(index); return scope.getStr(index);
} }
} }
@@ -7,16 +7,16 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables.reference; 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.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.tokenizer.Position; import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public abstract class VariableReferenceNode<T> implements Returnable<T> { public abstract class VariableReferenceNode<T> implements Expression<T> {
protected final int index; protected final int index;
private final Position position; private final SourcePosition position;
private final ReturnType type; private final ReturnType type;
public VariableReferenceNode(Position position, ReturnType type, int index) { public VariableReferenceNode(SourcePosition position, ReturnType type, int index) {
this.position = position; this.position = position;
this.type = type; this.type = type;
this.index = index; this.index = index;
@@ -28,7 +28,7 @@ public abstract class VariableReferenceNode<T> implements Returnable<T> {
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
} }
@@ -7,6 +7,8 @@
package com.dfsek.terra.addons.terrascript.script; package com.dfsek.terra.addons.terrascript.script;
import com.dfsek.terra.addons.terrascript.lexer.Lexer;
import net.jafama.FastMath; import net.jafama.FastMath;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -19,7 +21,8 @@ import java.util.Random;
import com.dfsek.terra.addons.terrascript.parser.Parser; 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.Executable;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope.ScopeBuilder;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; 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.BinaryNumberFunctionBuilder;
import com.dfsek.terra.addons.terrascript.script.builders.BiomeFunctionBuilder; import com.dfsek.terra.addons.terrascript.script.builders.BiomeFunctionBuilder;
@@ -51,32 +54,34 @@ import com.dfsek.terra.api.world.WritableWorld;
public class StructureScript implements Structure, Keyed<StructureScript> { public class StructureScript implements Structure, Keyed<StructureScript> {
private static final Logger LOGGER = LoggerFactory.getLogger(StructureScript.class); private static final Logger LOGGER = LoggerFactory.getLogger(StructureScript.class);
private final Executable block; private final Executable executable;
private final RegistryKey id; private final RegistryKey id;
private final String profile; private final String profile;
private final Platform platform; private final Platform platform;
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public StructureScript(InputStream inputStream, RegistryKey id, Platform platform, Registry<Structure> registry, public StructureScript(InputStream source, RegistryKey id, Platform platform, Registry<Structure> structureRegistry,
Registry<LootTable> lootRegistry, Registry<LootTable> lootRegistry,
Registry<FunctionBuilder> functionRegistry) { Registry<FunctionBuilder> functionRegistry) {
Parser parser; Lexer lexer;
try { try {
parser = new Parser(IOUtils.toString(inputStream, Charset.defaultCharset())); lexer = new Lexer(IOUtils.toString(source, Charset.defaultCharset()));
} catch(IOException e) { } catch(IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
Parser parser = new Parser(lexer);
this.id = id; this.id = id;
this.profile = "terrascript_direct:" + id; this.profile = "terrascript_direct:" + id;
//noinspection unchecked ScopeBuilder scope = new ScopeBuilder();
functionRegistry.forEach((key, function) -> parser.registerFunction(key.getID(), function)); // Register registry functions.
parser functionRegistry.forEach((key, function) -> scope.registerFunction(key.getID(), function)); // Register registry functions.
scope
.registerFunction("block", new BlockFunctionBuilder(platform)) .registerFunction("block", new BlockFunctionBuilder(platform))
.registerFunction("debugBlock", new BlockFunctionBuilder(platform)) .registerFunction("debugBlock", new BlockFunctionBuilder(platform))
.registerFunction("structure", new StructureFunctionBuilder(registry, platform)) .registerFunction("structure", new StructureFunctionBuilder(structureRegistry, platform))
.registerFunction("randomInt", new RandomFunctionBuilder()) .registerFunction("randomInt", new RandomFunctionBuilder())
.registerFunction("recursions", new RecursionsFunctionBuilder()) .registerFunction("recursions", new RecursionsFunctionBuilder())
.registerFunction("setMark", new SetMarkFunctionBuilder()) .registerFunction("setMark", new SetMarkFunctionBuilder())
@@ -89,15 +94,15 @@ public class StructureScript implements Structure, Keyed<StructureScript> {
.registerFunction("state", new StateFunctionBuilder(platform)) .registerFunction("state", new StateFunctionBuilder(platform))
.registerFunction("setWaterlog", new UnaryBooleanFunctionBuilder((waterlog, args) -> args.setWaterlog(waterlog))) .registerFunction("setWaterlog", new UnaryBooleanFunctionBuilder((waterlog, args) -> args.setWaterlog(waterlog)))
.registerFunction("originX", new ZeroArgFunctionBuilder<Number>(arguments -> arguments.getOrigin().getX(), .registerFunction("originX", new ZeroArgFunctionBuilder<Number>(arguments -> arguments.getOrigin().getX(),
Returnable.ReturnType.NUMBER)) Expression.ReturnType.NUMBER))
.registerFunction("originY", new ZeroArgFunctionBuilder<Number>(arguments -> arguments.getOrigin().getY(), .registerFunction("originY", new ZeroArgFunctionBuilder<Number>(arguments -> arguments.getOrigin().getY(),
Returnable.ReturnType.NUMBER)) Expression.ReturnType.NUMBER))
.registerFunction("originZ", new ZeroArgFunctionBuilder<Number>(arguments -> arguments.getOrigin().getZ(), .registerFunction("originZ", new ZeroArgFunctionBuilder<Number>(arguments -> arguments.getOrigin().getZ(),
Returnable.ReturnType.NUMBER)) Expression.ReturnType.NUMBER))
.registerFunction("rotation", new ZeroArgFunctionBuilder<>(arguments -> arguments.getRotation().toString(), .registerFunction("rotation", new ZeroArgFunctionBuilder<>(arguments -> arguments.getRotation().toString(),
Returnable.ReturnType.STRING)) Expression.ReturnType.STRING))
.registerFunction("rotationDegrees", new ZeroArgFunctionBuilder<>(arguments -> arguments.getRotation().getDegrees(), .registerFunction("rotationDegrees", new ZeroArgFunctionBuilder<>(arguments -> arguments.getRotation().getDegrees(),
Returnable.ReturnType.NUMBER)) Expression.ReturnType.NUMBER))
.registerFunction("print", .registerFunction("print",
new UnaryStringFunctionBuilder(string -> LOGGER.info("[TerraScript:{}] {}", id, string))) new UnaryStringFunctionBuilder(string -> LOGGER.info("[TerraScript:{}] {}", id, string)))
.registerFunction("abs", new UnaryNumberFunctionBuilder(number -> FastMath.abs(number.doubleValue()))) .registerFunction("abs", new UnaryNumberFunctionBuilder(number -> FastMath.abs(number.doubleValue())))
@@ -120,11 +125,7 @@ public class StructureScript implements Structure, Keyed<StructureScript> {
.registerFunction("min", new BinaryNumberFunctionBuilder( .registerFunction("min", new BinaryNumberFunctionBuilder(
(number, number2) -> FastMath.min(number.doubleValue(), number2.doubleValue()))); (number, number2) -> FastMath.min(number.doubleValue(), number2.doubleValue())));
if(!platform.getTerraConfig().isDebugScript()) { executable = parser.parse(scope);
parser.ignoreFunction("debugBlock");
}
block = parser.parse();
this.platform = platform; this.platform = platform;
} }
@@ -132,21 +133,21 @@ public class StructureScript implements Structure, Keyed<StructureScript> {
@SuppressWarnings("try") @SuppressWarnings("try")
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) { public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
platform.getProfiler().push(profile); platform.getProfiler().push(profile);
boolean result = applyBlock(new TerraImplementationArguments(location, rotation, random, world, 0)); boolean result = execute(new TerraImplementationArguments(location, rotation, random, world, 0));
platform.getProfiler().pop(profile); platform.getProfiler().pop(profile);
return result; return result;
} }
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation, int recursions) { public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation, int recursions) {
platform.getProfiler().push(profile); platform.getProfiler().push(profile);
boolean result = applyBlock(new TerraImplementationArguments(location, rotation, random, world, recursions)); boolean result = execute(new TerraImplementationArguments(location, rotation, random, world, recursions));
platform.getProfiler().pop(profile); platform.getProfiler().pop(profile);
return result; return result;
} }
private boolean applyBlock(TerraImplementationArguments arguments) { private boolean execute(TerraImplementationArguments arguments) {
try { try {
return block.execute(arguments); return executable.execute(arguments);
} catch(RuntimeException e) { } catch(RuntimeException e) {
LOGGER.error("Failed to generate structure at {}", arguments.getOrigin(), e); LOGGER.error("Failed to generate structure at {}", arguments.getOrigin(), e);
return false; return false;
@@ -10,12 +10,12 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List; import java.util.List;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; 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.functions.FunctionBuilder;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BinaryNumberFunctionBuilder implements FunctionBuilder<Function<Number>> { public class BinaryNumberFunctionBuilder implements FunctionBuilder<Function<Number>> {
@@ -27,7 +27,7 @@ public class BinaryNumberFunctionBuilder implements FunctionBuilder<Function<Num
} }
@Override @Override
public Function<Number> build(List<Returnable<?>> argumentList, Position position) { public Function<Number> build(List<Expression<?>> argumentList, SourcePosition position) {
return new Function<>() { return new Function<>() {
@Override @Override
public ReturnType returnType() { public ReturnType returnType() {
@@ -36,13 +36,13 @@ public class BinaryNumberFunctionBuilder implements FunctionBuilder<Function<Num
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) { public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return function.apply(((Returnable<Number>) argumentList.get(0)).apply(implementationArguments, scope), return function.apply(((Expression<Number>) argumentList.get(0)).evaluate(implementationArguments, scope),
((Returnable<Number>) argumentList.get(1)).apply(implementationArguments, scope)); ((Expression<Number>) argumentList.get(1)).evaluate(implementationArguments, scope));
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
}; };
@@ -54,8 +54,8 @@ public class BinaryNumberFunctionBuilder implements FunctionBuilder<Function<Num
} }
@Override @Override
public Returnable.ReturnType getArgument(int position) { public Expression.ReturnType getArgument(int position) {
if(position == 0 || position == 1) return Returnable.ReturnType.NUMBER; if(position == 0 || position == 1) return Expression.ReturnType.NUMBER;
return null; return null;
} }
} }
@@ -9,10 +9,10 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; 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.script.functions.BiomeFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform; import com.dfsek.terra.api.Platform;
@@ -25,9 +25,9 @@ public class BiomeFunctionBuilder implements FunctionBuilder<BiomeFunction> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public BiomeFunction build(List<Returnable<?>> argumentList, Position position) { public BiomeFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new BiomeFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1), return new BiomeFunction((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), position); (Expression<Number>) argumentList.get(2), position);
} }
@Override @Override
@@ -36,9 +36,9 @@ public class BiomeFunctionBuilder implements FunctionBuilder<BiomeFunction> {
} }
@Override @Override
public Returnable.ReturnType getArgument(int position) { public Expression.ReturnType getArgument(int position) {
return switch(position) { return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER; case 0, 1, 2 -> Expression.ReturnType.NUMBER;
default -> null; default -> null;
}; };
} }
@@ -9,13 +9,13 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; 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.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.constants.BooleanConstant; 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.constants.StringConstant;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; 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.script.functions.BlockFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform; import com.dfsek.terra.api.Platform;
@@ -28,17 +28,17 @@ public class BlockFunctionBuilder implements FunctionBuilder<BlockFunction> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public BlockFunction build(List<Returnable<?>> argumentList, Position position) { public BlockFunction build(List<Expression<?>> argumentList, SourcePosition position) {
if(argumentList.size() < 4) throw new ParseException("Expected data", position); if(argumentList.size() < 4) throw new ParseException("Expected data", position);
Returnable<Boolean> booleanReturnable = new BooleanConstant(true, position); Expression<Boolean> booleanReturnable = new BooleanConstant(true, position);
if(argumentList.size() == 5) booleanReturnable = (Returnable<Boolean>) argumentList.get(4); if(argumentList.size() == 5) booleanReturnable = (Expression<Boolean>) argumentList.get(4);
if(argumentList.get(3) instanceof StringConstant) { if(argumentList.get(3) instanceof StringConstant) {
return new BlockFunction.Constant((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1), return new BlockFunction.Constant((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (StringConstant) argumentList.get(3), (Expression<Number>) argumentList.get(2), (StringConstant) argumentList.get(3),
booleanReturnable, platform, position); booleanReturnable, platform, position);
} }
return new BlockFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1), return new BlockFunction((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) argumentList.get(3), booleanReturnable, (Expression<Number>) argumentList.get(2), (Expression<String>) argumentList.get(3), booleanReturnable,
platform, position); platform, position);
} }
@@ -48,11 +48,11 @@ public class BlockFunctionBuilder implements FunctionBuilder<BlockFunction> {
} }
@Override @Override
public Returnable.ReturnType getArgument(int position) { public Expression.ReturnType getArgument(int position) {
return switch(position) { return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER; case 0, 1, 2 -> Expression.ReturnType.NUMBER;
case 3 -> Returnable.ReturnType.STRING; case 3 -> Expression.ReturnType.STRING;
case 4 -> Returnable.ReturnType.BOOLEAN; case 4 -> Expression.ReturnType.BOOLEAN;
default -> null; default -> null;
}; };
} }
@@ -9,18 +9,18 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; 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.script.functions.CheckBlockFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class CheckBlockFunctionBuilder implements FunctionBuilder<CheckBlockFunction> { public class CheckBlockFunctionBuilder implements FunctionBuilder<CheckBlockFunction> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public CheckBlockFunction build(List<Returnable<?>> argumentList, Position position) { public CheckBlockFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new CheckBlockFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1), return new CheckBlockFunction((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), position); (Expression<Number>) argumentList.get(2), position);
} }
@Override @Override
@@ -29,9 +29,9 @@ public class CheckBlockFunctionBuilder implements FunctionBuilder<CheckBlockFunc
} }
@Override @Override
public Returnable.ReturnType getArgument(int position) { public Expression.ReturnType getArgument(int position) {
return switch(position) { return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER; case 0, 1, 2 -> Expression.ReturnType.NUMBER;
default -> null; default -> null;
}; };
} }
@@ -9,10 +9,10 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; 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.script.functions.EntityFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform; import com.dfsek.terra.api.Platform;
@@ -25,9 +25,9 @@ public class EntityFunctionBuilder implements FunctionBuilder<EntityFunction> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public EntityFunction build(List<Returnable<?>> argumentList, Position position) { public EntityFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new EntityFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1), return new EntityFunction((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) argumentList.get(3), platform, position); (Expression<Number>) argumentList.get(2), (Expression<String>) argumentList.get(3), platform, position);
} }
@Override @Override
@@ -36,10 +36,10 @@ public class EntityFunctionBuilder implements FunctionBuilder<EntityFunction> {
} }
@Override @Override
public Returnable.ReturnType getArgument(int position) { public Expression.ReturnType getArgument(int position) {
return switch(position) { return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER; case 0, 1, 2 -> Expression.ReturnType.NUMBER;
case 3 -> Returnable.ReturnType.STRING; case 3 -> Expression.ReturnType.STRING;
default -> null; default -> null;
}; };
} }
@@ -9,10 +9,10 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; 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.script.functions.GetMarkFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class GetMarkFunctionBuilder implements FunctionBuilder<GetMarkFunction> { public class GetMarkFunctionBuilder implements FunctionBuilder<GetMarkFunction> {
@@ -22,9 +22,9 @@ public class GetMarkFunctionBuilder implements FunctionBuilder<GetMarkFunction>
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public GetMarkFunction build(List<Returnable<?>> argumentList, Position position) { public GetMarkFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new GetMarkFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1), return new GetMarkFunction((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), position); (Expression<Number>) argumentList.get(2), position);
} }
@Override @Override
@@ -33,9 +33,9 @@ public class GetMarkFunctionBuilder implements FunctionBuilder<GetMarkFunction>
} }
@Override @Override
public Returnable.ReturnType getArgument(int position) { public Expression.ReturnType getArgument(int position) {
return switch(position) { return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER; case 0, 1, 2 -> Expression.ReturnType.NUMBER;
default -> null; default -> null;
}; };
} }
@@ -9,11 +9,11 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; 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.StructureScript;
import com.dfsek.terra.addons.terrascript.script.functions.LootFunction; 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.Platform;
import com.dfsek.terra.api.registry.Registry; import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.structure.LootTable; import com.dfsek.terra.api.structure.LootTable;
@@ -32,9 +32,9 @@ public class LootFunctionBuilder implements FunctionBuilder<LootFunction> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public LootFunction build(List<Returnable<?>> argumentList, Position position) { public LootFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new LootFunction(registry, (Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1), return new LootFunction(registry, (Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) argumentList.get(3), platform, position, (Expression<Number>) argumentList.get(2), (Expression<String>) argumentList.get(3), platform, position,
script); script);
} }
@@ -44,10 +44,10 @@ public class LootFunctionBuilder implements FunctionBuilder<LootFunction> {
} }
@Override @Override
public Returnable.ReturnType getArgument(int position) { public Expression.ReturnType getArgument(int position) {
return switch(position) { return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER; case 0, 1, 2 -> Expression.ReturnType.NUMBER;
case 3 -> Returnable.ReturnType.STRING; case 3 -> Expression.ReturnType.STRING;
default -> null; default -> null;
}; };
} }
@@ -9,10 +9,10 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; 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.script.functions.PullFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform; import com.dfsek.terra.api.Platform;
@@ -25,9 +25,9 @@ public class PullFunctionBuilder implements FunctionBuilder<PullFunction> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public PullFunction build(List<Returnable<?>> argumentList, Position position) { public PullFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new PullFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1), return new PullFunction((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) argumentList.get(3), platform, position); (Expression<Number>) argumentList.get(2), (Expression<String>) argumentList.get(3), platform, position);
} }
@Override @Override
@@ -36,10 +36,10 @@ public class PullFunctionBuilder implements FunctionBuilder<PullFunction> {
} }
@Override @Override
public Returnable.ReturnType getArgument(int position) { public Expression.ReturnType getArgument(int position) {
return switch(position) { return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER; case 0, 1, 2 -> Expression.ReturnType.NUMBER;
case 3 -> Returnable.ReturnType.STRING; case 3 -> Expression.ReturnType.STRING;
default -> null; default -> null;
}; };
} }
@@ -9,17 +9,17 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; 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.script.functions.RandomFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class RandomFunctionBuilder implements FunctionBuilder<RandomFunction> { public class RandomFunctionBuilder implements FunctionBuilder<RandomFunction> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public RandomFunction build(List<Returnable<?>> argumentList, Position position) { public RandomFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new RandomFunction((Returnable<Number>) argumentList.get(0), position); return new RandomFunction((Expression<Number>) argumentList.get(0), position);
} }
@Override @Override
@@ -28,8 +28,8 @@ public class RandomFunctionBuilder implements FunctionBuilder<RandomFunction> {
} }
@Override @Override
public Returnable.ReturnType getArgument(int position) { public Expression.ReturnType getArgument(int position) {
if(position == 0) return Returnable.ReturnType.NUMBER; if(position == 0) return Expression.ReturnType.NUMBER;
return null; return null;
} }
} }
@@ -9,15 +9,15 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; 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.script.functions.RecursionsFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class RecursionsFunctionBuilder implements FunctionBuilder<RecursionsFunction> { public class RecursionsFunctionBuilder implements FunctionBuilder<RecursionsFunction> {
@Override @Override
public RecursionsFunction build(List<Returnable<?>> argumentList, Position position) { public RecursionsFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new RecursionsFunction(position); return new RecursionsFunction(position);
} }
@@ -27,7 +27,7 @@ public class RecursionsFunctionBuilder implements FunctionBuilder<RecursionsFunc
} }
@Override @Override
public Returnable.ReturnType getArgument(int position) { public Expression.ReturnType getArgument(int position) {
return null; return null;
} }
} }
@@ -9,10 +9,10 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable; import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; 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.script.functions.SetMarkFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class SetMarkFunctionBuilder implements FunctionBuilder<SetMarkFunction> { public class SetMarkFunctionBuilder implements FunctionBuilder<SetMarkFunction> {
@@ -22,9 +22,9 @@ public class SetMarkFunctionBuilder implements FunctionBuilder<SetMarkFunction>
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public SetMarkFunction build(List<Returnable<?>> argumentList, Position position) { public SetMarkFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new SetMarkFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1), return new SetMarkFunction((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) argumentList.get(3), position); (Expression<Number>) argumentList.get(2), (Expression<String>) argumentList.get(3), position);
} }
@Override @Override
@@ -33,10 +33,10 @@ public class SetMarkFunctionBuilder implements FunctionBuilder<SetMarkFunction>
} }
@Override @Override
public Returnable.ReturnType getArgument(int position) { public Expression.ReturnType getArgument(int position) {
return switch(position) { return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER; case 0, 1, 2 -> Expression.ReturnType.NUMBER;
case 3 -> Returnable.ReturnType.STRING; case 3 -> Expression.ReturnType.STRING;
default -> null; default -> null;
}; };
} }
@@ -9,11 +9,11 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; 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.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; 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.script.functions.StateFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform; import com.dfsek.terra.api.Platform;
@@ -26,10 +26,10 @@ public class StateFunctionBuilder implements FunctionBuilder<StateFunction> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public StateFunction build(List<Returnable<?>> argumentList, Position position) { public StateFunction build(List<Expression<?>> argumentList, SourcePosition position) {
if(argumentList.size() < 4) throw new ParseException("Expected data", position); if(argumentList.size() < 4) throw new ParseException("Expected data", position);
return new StateFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1), return new StateFunction((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) argumentList.get(3), position); (Expression<Number>) argumentList.get(2), (Expression<String>) argumentList.get(3), position);
} }
@Override @Override
@@ -38,10 +38,10 @@ public class StateFunctionBuilder implements FunctionBuilder<StateFunction> {
} }
@Override @Override
public Returnable.ReturnType getArgument(int position) { public Expression.ReturnType getArgument(int position) {
return switch(position) { return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER; case 0, 1, 2 -> Expression.ReturnType.NUMBER;
case 3 -> Returnable.ReturnType.STRING; case 3 -> Expression.ReturnType.STRING;
default -> null; default -> null;
}; };
} }
@@ -10,11 +10,11 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; 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.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder; 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.script.functions.StructureFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform; import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.registry.Registry; import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.structure.Structure; import com.dfsek.terra.api.structure.Structure;
@@ -31,12 +31,12 @@ public class StructureFunctionBuilder implements FunctionBuilder<StructureFuncti
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public StructureFunction build(List<Returnable<?>> argumentList, Position position) { public StructureFunction build(List<Expression<?>> argumentList, SourcePosition position) {
if(argumentList.size() < 5) throw new ParseException("Expected rotations", position); if(argumentList.size() < 5) throw new ParseException("Expected rotations", position);
return new StructureFunction((Returnable<Number>) argumentList.remove(0), (Returnable<Number>) argumentList.remove(0), return new StructureFunction((Expression<Number>) argumentList.remove(0), (Expression<Number>) argumentList.remove(0),
(Returnable<Number>) argumentList.remove(0), (Returnable<String>) argumentList.remove(0), (Expression<Number>) argumentList.remove(0), (Expression<String>) argumentList.remove(0),
argumentList.stream().map(item -> ((Returnable<String>) item)).collect(Collectors.toList()), registry, argumentList.stream().map(item -> ((Expression<String>) item)).collect(Collectors.toList()), registry,
position, platform); position, platform);
} }
@@ -46,10 +46,10 @@ public class StructureFunctionBuilder implements FunctionBuilder<StructureFuncti
} }
@Override @Override
public Returnable.ReturnType getArgument(int position) { public Expression.ReturnType getArgument(int position) {
return switch(position) { return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER; case 0, 1, 2 -> Expression.ReturnType.NUMBER;
default -> Returnable.ReturnType.STRING; default -> Expression.ReturnType.STRING;
}; };
} }
} }
@@ -10,13 +10,13 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List; import java.util.List;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; 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.functions.FunctionBuilder;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class UnaryBooleanFunctionBuilder implements FunctionBuilder<Function<Void>> { public class UnaryBooleanFunctionBuilder implements FunctionBuilder<Function<Void>> {
@@ -28,7 +28,7 @@ public class UnaryBooleanFunctionBuilder implements FunctionBuilder<Function<Voi
} }
@Override @Override
public Function<Void> build(List<Returnable<?>> argumentList, Position position) { public Function<Void> build(List<Expression<?>> argumentList, SourcePosition position) {
return new Function<>() { return new Function<>() {
@Override @Override
public ReturnType returnType() { public ReturnType returnType() {
@@ -37,14 +37,14 @@ public class UnaryBooleanFunctionBuilder implements FunctionBuilder<Function<Voi
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public Void apply(ImplementationArguments implementationArguments, Scope scope) { public Void evaluate(ImplementationArguments implementationArguments, Scope scope) {
function.accept(((Returnable<Boolean>) argumentList.get(0)).apply(implementationArguments, scope), function.accept(((Expression<Boolean>) argumentList.get(0)).evaluate(implementationArguments, scope),
(TerraImplementationArguments) implementationArguments); (TerraImplementationArguments) implementationArguments);
return null; return null;
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
}; };
@@ -56,8 +56,8 @@ public class UnaryBooleanFunctionBuilder implements FunctionBuilder<Function<Voi
} }
@Override @Override
public Returnable.ReturnType getArgument(int position) { public Expression.ReturnType getArgument(int position) {
if(position == 0) return Returnable.ReturnType.BOOLEAN; if(position == 0) return Expression.ReturnType.BOOLEAN;
return null; return null;
} }
} }
@@ -9,12 +9,12 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; 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.functions.FunctionBuilder;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class UnaryNumberFunctionBuilder implements FunctionBuilder<Function<Number>> { public class UnaryNumberFunctionBuilder implements FunctionBuilder<Function<Number>> {
@@ -26,7 +26,7 @@ public class UnaryNumberFunctionBuilder implements FunctionBuilder<Function<Numb
} }
@Override @Override
public Function<Number> build(List<Returnable<?>> argumentList, Position position) { public Function<Number> build(List<Expression<?>> argumentList, SourcePosition position) {
return new Function<>() { return new Function<>() {
@Override @Override
public ReturnType returnType() { public ReturnType returnType() {
@@ -35,12 +35,12 @@ public class UnaryNumberFunctionBuilder implements FunctionBuilder<Function<Numb
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) { public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return function.apply(((Returnable<Number>) argumentList.get(0)).apply(implementationArguments, scope)); return function.apply(((Expression<Number>) argumentList.get(0)).evaluate(implementationArguments, scope));
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
}; };
@@ -52,8 +52,8 @@ public class UnaryNumberFunctionBuilder implements FunctionBuilder<Function<Numb
} }
@Override @Override
public Returnable.ReturnType getArgument(int position) { public Expression.ReturnType getArgument(int position) {
if(position == 0) return Returnable.ReturnType.NUMBER; if(position == 0) return Expression.ReturnType.NUMBER;
return null; return null;
} }
} }
@@ -9,12 +9,12 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; 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.functions.FunctionBuilder;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class UnaryStringFunctionBuilder implements FunctionBuilder<Function<Void>> { public class UnaryStringFunctionBuilder implements FunctionBuilder<Function<Void>> {
@@ -26,7 +26,7 @@ public class UnaryStringFunctionBuilder implements FunctionBuilder<Function<Void
} }
@Override @Override
public Function<Void> build(List<Returnable<?>> argumentList, Position position) { public Function<Void> build(List<Expression<?>> argumentList, SourcePosition position) {
return new Function<>() { return new Function<>() {
@Override @Override
public ReturnType returnType() { public ReturnType returnType() {
@@ -35,13 +35,13 @@ public class UnaryStringFunctionBuilder implements FunctionBuilder<Function<Void
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public Void apply(ImplementationArguments implementationArguments, Scope scope) { public Void evaluate(ImplementationArguments implementationArguments, Scope scope) {
function.accept(((Returnable<String>) argumentList.get(0)).apply(implementationArguments, scope)); function.accept(((Expression<String>) argumentList.get(0)).evaluate(implementationArguments, scope));
return null; return null;
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
}; };
@@ -53,8 +53,8 @@ public class UnaryStringFunctionBuilder implements FunctionBuilder<Function<Void
} }
@Override @Override
public Returnable.ReturnType getArgument(int position) { public Expression.ReturnType getArgument(int position) {
if(position == 0) return Returnable.ReturnType.STRING; if(position == 0) return Expression.ReturnType.STRING;
return null; return null;
} }
} }
@@ -9,26 +9,26 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List; import java.util.List;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; 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.functions.FunctionBuilder;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ZeroArgFunctionBuilder<T> implements FunctionBuilder<Function<T>> { public class ZeroArgFunctionBuilder<T> implements FunctionBuilder<Function<T>> {
private final java.util.function.Function<TerraImplementationArguments, T> function; private final java.util.function.Function<TerraImplementationArguments, T> function;
private final Returnable.ReturnType type; private final Expression.ReturnType type;
public ZeroArgFunctionBuilder(java.util.function.Function<TerraImplementationArguments, T> function, Returnable.ReturnType type) { public ZeroArgFunctionBuilder(java.util.function.Function<TerraImplementationArguments, T> function, Expression.ReturnType type) {
this.function = function; this.function = function;
this.type = type; this.type = type;
} }
@Override @Override
public Function<T> build(List<Returnable<?>> argumentList, Position position) { public Function<T> build(List<Expression<?>> argumentList, SourcePosition position) {
return new Function<>() { return new Function<>() {
@Override @Override
public ReturnType returnType() { public ReturnType returnType() {
@@ -36,12 +36,12 @@ public class ZeroArgFunctionBuilder<T> implements FunctionBuilder<Function<T>> {
} }
@Override @Override
public T apply(ImplementationArguments implementationArguments, Scope scope) { public T evaluate(ImplementationArguments implementationArguments, Scope scope) {
return function.apply((TerraImplementationArguments) implementationArguments); return function.apply((TerraImplementationArguments) implementationArguments);
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
}; };
@@ -53,7 +53,7 @@ public class ZeroArgFunctionBuilder<T> implements FunctionBuilder<Function<T>> {
} }
@Override @Override
public Returnable.ReturnType getArgument(int position) { public Expression.ReturnType getArgument(int position) {
if(position == 0) return type; if(position == 0) return type;
return null; return null;
} }
@@ -9,12 +9,12 @@ package com.dfsek.terra.addons.terrascript.script.functions;
import net.jafama.FastMath; import net.jafama.FastMath;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; 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.RotationUtil;
import com.dfsek.terra.api.util.vector.Vector2; import com.dfsek.terra.api.util.vector.Vector2;
import com.dfsek.terra.api.util.vector.Vector3; import com.dfsek.terra.api.util.vector.Vector3;
@@ -22,11 +22,11 @@ import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class BiomeFunction implements Function<String> { public class BiomeFunction implements Function<String> {
private final Returnable<Number> x, y, z; private final Expression<Number> x, y, z;
private final Position position; private final SourcePosition position;
public BiomeFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Position position) { public BiomeFunction(Expression<Number> x, Expression<Number> y, Expression<Number> z, SourcePosition position) {
this.x = x; this.x = x;
this.y = y; this.y = y;
this.z = z; this.z = z;
@@ -35,11 +35,11 @@ public class BiomeFunction implements Function<String> {
@Override @Override
public String apply(ImplementationArguments implementationArguments, Scope scope) { public String evaluate(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments; TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(), Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.evaluate(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()), z.evaluate(implementationArguments, scope).doubleValue()),
arguments.getRotation()); arguments.getRotation());
@@ -49,12 +49,12 @@ public class BiomeFunction implements Function<String> {
.toVector3() .toVector3()
.mutable() .mutable()
.add(Vector3.of(FastMath.roundToInt(xz.getX()), .add(Vector3.of(FastMath.roundToInt(xz.getX()),
y.apply(implementationArguments, scope).intValue(), y.evaluate(implementationArguments, scope).intValue(),
FastMath.roundToInt(xz.getZ()))).immutable(), arguments.getWorld().getSeed()).getID(); FastMath.roundToInt(xz.getZ()))).immutable(), arguments.getWorld().getSeed()).getID();
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
@@ -14,13 +14,13 @@ import org.slf4j.LoggerFactory;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.constants.StringConstant; 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.Function;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; 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.Platform;
import com.dfsek.terra.api.block.state.BlockState; import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.util.RotationUtil; import com.dfsek.terra.api.util.RotationUtil;
@@ -30,15 +30,15 @@ import com.dfsek.terra.api.util.vector.Vector3;
public class BlockFunction implements Function<Void> { public class BlockFunction implements Function<Void> {
private static final Logger logger = LoggerFactory.getLogger(BlockFunction.class); private static final Logger logger = LoggerFactory.getLogger(BlockFunction.class);
protected final Returnable<Number> x, y, z; protected final Expression<Number> x, y, z;
protected final Returnable<String> blockData; protected final Expression<String> blockData;
protected final Platform platform; protected final Platform platform;
private final Map<String, BlockState> data = new HashMap<>(); private final Map<String, BlockState> data = new HashMap<>();
private final Returnable<Boolean> overwrite; private final Expression<Boolean> overwrite;
private final Position position; private final SourcePosition position;
public BlockFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Returnable<String> blockData, public BlockFunction(Expression<Number> x, Expression<Number> y, Expression<Number> z, Expression<String> blockData,
Returnable<Boolean> overwrite, Platform platform, Position position) { Expression<Boolean> overwrite, Platform platform, SourcePosition position) {
this.x = x; this.x = x;
this.y = y; this.y = y;
this.z = z; this.z = z;
@@ -49,7 +49,7 @@ public class BlockFunction implements Function<Void> {
} }
@Override @Override
public Void apply(ImplementationArguments implementationArguments, Scope scope) { public Void evaluate(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments; TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
BlockState rot = getBlockState(implementationArguments, scope); BlockState rot = getBlockState(implementationArguments, scope);
setBlock(implementationArguments, scope, arguments, rot); setBlock(implementationArguments, scope, arguments, rot);
@@ -57,7 +57,7 @@ public class BlockFunction implements Function<Void> {
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
@@ -68,14 +68,15 @@ public class BlockFunction implements Function<Void> {
void setBlock(ImplementationArguments implementationArguments, Scope scope, void setBlock(ImplementationArguments implementationArguments, Scope scope,
TerraImplementationArguments arguments, BlockState rot) { TerraImplementationArguments arguments, BlockState rot) {
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(), Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.evaluate(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()), arguments.getRotation()); z.evaluate(implementationArguments, scope).doubleValue()),
arguments.getRotation());
try { try {
Vector3.Mutable set = Vector3.of(FastMath.roundToInt(xz.getX()), Vector3.Mutable set = Vector3.of(FastMath.roundToInt(xz.getX()),
y.apply(implementationArguments, scope).doubleValue(), y.evaluate(implementationArguments, scope).doubleValue(),
FastMath.roundToInt(xz.getZ())).mutable().add(arguments.getOrigin()); FastMath.roundToInt(xz.getZ())).mutable().add(arguments.getOrigin());
BlockState current = arguments.getWorld().getBlockState(set); BlockState current = arguments.getWorld().getBlockState(set);
if(overwrite.apply(implementationArguments, scope) || current.isAir()) { if(overwrite.evaluate(implementationArguments, scope) || current.isAir()) {
arguments.getWorld().setBlockState(set, rot); arguments.getWorld().setBlockState(set, rot);
} }
} catch(RuntimeException e) { } catch(RuntimeException e) {
@@ -84,15 +85,15 @@ public class BlockFunction implements Function<Void> {
} }
protected BlockState getBlockState(ImplementationArguments arguments, Scope scope) { protected BlockState getBlockState(ImplementationArguments arguments, Scope scope) {
return data.computeIfAbsent(blockData.apply(arguments, scope), platform.getWorldHandle()::createBlockState); return data.computeIfAbsent(blockData.evaluate(arguments, scope), platform.getWorldHandle()::createBlockState);
} }
public static class Constant extends BlockFunction { public static class Constant extends BlockFunction {
private final BlockState state; private final BlockState state;
public Constant(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, StringConstant blockData, public Constant(Expression<Number> x, Expression<Number> y, Expression<Number> z, StringConstant blockData,
Returnable<Boolean> overwrite, Platform platform, Position position) { Expression<Boolean> overwrite, Platform platform, SourcePosition position) {
super(x, y, z, blockData, overwrite, platform, position); super(x, y, z, blockData, overwrite, platform, position);
this.state = platform.getWorldHandle().createBlockState(blockData.getConstant()); this.state = platform.getWorldHandle().createBlockState(blockData.getConstant());
} }
@@ -9,22 +9,22 @@ package com.dfsek.terra.addons.terrascript.script.functions;
import net.jafama.FastMath; import net.jafama.FastMath;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; 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.RotationUtil;
import com.dfsek.terra.api.util.vector.Vector2; import com.dfsek.terra.api.util.vector.Vector2;
import com.dfsek.terra.api.util.vector.Vector3; import com.dfsek.terra.api.util.vector.Vector3;
public class CheckBlockFunction implements Function<String> { public class CheckBlockFunction implements Function<String> {
private final Returnable<Number> x, y, z; private final Expression<Number> x, y, z;
private final Position position; private final SourcePosition position;
public CheckBlockFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Position position) { public CheckBlockFunction(Expression<Number> x, Expression<Number> y, Expression<Number> z, SourcePosition position) {
this.x = x; this.x = x;
this.y = y; this.y = y;
this.z = z; this.z = z;
@@ -33,11 +33,11 @@ public class CheckBlockFunction implements Function<String> {
@Override @Override
public String apply(ImplementationArguments implementationArguments, Scope scope) { public String evaluate(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments; TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(), Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.evaluate(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()), z.evaluate(implementationArguments, scope).doubleValue()),
arguments.getRotation()); arguments.getRotation());
@@ -46,7 +46,7 @@ public class CheckBlockFunction implements Function<String> {
.toVector3() .toVector3()
.mutable() .mutable()
.add(Vector3.of(FastMath.roundToInt(xz.getX()), .add(Vector3.of(FastMath.roundToInt(xz.getX()),
y.apply(implementationArguments, scope) y.evaluate(implementationArguments, scope)
.doubleValue(), FastMath.roundToInt(xz.getZ())))) .doubleValue(), FastMath.roundToInt(xz.getZ()))))
.getAsString(); .getAsString();
if(data.contains("[")) return data.substring(0, data.indexOf('[')); // Strip properties if(data.contains("[")) return data.substring(0, data.indexOf('[')); // Strip properties
@@ -54,7 +54,7 @@ public class CheckBlockFunction implements Function<String> {
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
@@ -7,14 +7,14 @@
package com.dfsek.terra.addons.terrascript.script.functions; package com.dfsek.terra.addons.terrascript.script.functions;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException; import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.constants.ConstantExpression; import com.dfsek.terra.addons.terrascript.parser.lang.constants.ConstantExpression;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; 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.Platform;
import com.dfsek.terra.api.entity.Entity; import com.dfsek.terra.api.entity.Entity;
import com.dfsek.terra.api.entity.EntityType; import com.dfsek.terra.api.entity.EntityType;
@@ -26,12 +26,12 @@ import com.dfsek.terra.api.util.vector.Vector3;
public class EntityFunction implements Function<Void> { public class EntityFunction implements Function<Void> {
private final EntityType data; private final EntityType data;
private final Returnable<Number> x, y, z; private final Expression<Number> x, y, z;
private final Position position; private final SourcePosition position;
private final Platform platform; private final Platform platform;
public EntityFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Returnable<String> data, Platform platform, public EntityFunction(Expression<Number> x, Expression<Number> y, Expression<Number> z, Expression<String> data, Platform platform,
Position position) { SourcePosition position) {
this.position = position; this.position = position;
this.platform = platform; this.platform = platform;
if(!(data instanceof ConstantExpression)) throw new ParseException("Entity data must be constant", data.getPosition()); if(!(data instanceof ConstantExpression)) throw new ParseException("Entity data must be constant", data.getPosition());
@@ -43,12 +43,13 @@ public class EntityFunction implements Function<Void> {
} }
@Override @Override
public Void apply(ImplementationArguments implementationArguments, Scope scope) { public Void evaluate(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments; TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(), Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.evaluate(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()), arguments.getRotation()); z.evaluate(implementationArguments, scope).doubleValue()),
arguments.getRotation());
Entity entity = arguments.getWorld().spawnEntity(Vector3.of(xz.getX(), y.apply(implementationArguments, scope).doubleValue(), Entity entity = arguments.getWorld().spawnEntity(Vector3.of(xz.getX(), y.evaluate(implementationArguments, scope).doubleValue(),
xz.getZ()) xz.getZ())
.mutable() .mutable()
.add(arguments.getOrigin()) .add(arguments.getOrigin())
@@ -59,7 +60,7 @@ public class EntityFunction implements Function<Void> {
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }
@@ -9,22 +9,22 @@ package com.dfsek.terra.addons.terrascript.script.functions;
import net.jafama.FastMath; import net.jafama.FastMath;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments; 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.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function; import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments; 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.RotationUtil;
import com.dfsek.terra.api.util.vector.Vector2; import com.dfsek.terra.api.util.vector.Vector2;
import com.dfsek.terra.api.util.vector.Vector3; import com.dfsek.terra.api.util.vector.Vector3;
public class GetMarkFunction implements Function<String> { public class GetMarkFunction implements Function<String> {
private final Returnable<Number> x, y, z; private final Expression<Number> x, y, z;
private final Position position; private final SourcePosition position;
public GetMarkFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Position position) { public GetMarkFunction(Expression<Number> x, Expression<Number> y, Expression<Number> z, SourcePosition position) {
this.position = position; this.position = position;
this.x = x; this.x = x;
this.y = y; this.y = y;
@@ -32,13 +32,14 @@ public class GetMarkFunction implements Function<String> {
} }
@Override @Override
public String apply(ImplementationArguments implementationArguments, Scope scope) { public String evaluate(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments; TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(), Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.evaluate(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()), arguments.getRotation()); z.evaluate(implementationArguments, scope).doubleValue()),
arguments.getRotation());
String mark = arguments.getMark(Vector3.of(FastMath.floorToInt(xz.getX()), FastMath.floorToInt( String mark = arguments.getMark(Vector3.of(FastMath.floorToInt(xz.getX()), FastMath.floorToInt(
y.apply(implementationArguments, scope).doubleValue()), y.evaluate(implementationArguments, scope).doubleValue()),
FastMath.floorToInt(xz.getZ())) FastMath.floorToInt(xz.getZ()))
.mutable() .mutable()
.add(arguments.getOrigin()) .add(arguments.getOrigin())
@@ -47,7 +48,7 @@ public class GetMarkFunction implements Function<String> {
} }
@Override @Override
public Position getPosition() { public SourcePosition getPosition() {
return position; return position;
} }

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