Compare commits

..

15 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
131 changed files with 2213 additions and 2514 deletions
@@ -1,4 +1,4 @@
version = version("1.2.0")
version = version("1.1.0")
dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
@@ -155,16 +155,6 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
return biomeProvider.getBiome(x, y, z, world.getSeed()).getContext().get(paletteInfoPropertyKey).paletteHolder().getPalette(y);
}
public double getSlant(int x, int y, int z, WorldProperties world, BiomeProvider biomeProvider) {
int fdX = FastMath.floorMod(x, 16);
int fdZ = FastMath.floorMod(z, 16);
return biomeProvider.getBiome(x, y, z, world.getSeed())
.getContext()
.get(paletteInfoPropertyKey)
.slantHolder()
.calculateSlant(samplerCache.get(x, z, world, biomeProvider), fdX, y, fdZ);
}
public SamplerProvider samplerProvider() {
return samplerCache;
}
@@ -14,7 +14,6 @@ import java.util.Map;
import java.util.function.Supplier;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.addons.noise.config.CubicSplinePointTemplate;
import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler;
import com.dfsek.terra.addons.noise.config.templates.BinaryArithmeticTemplate;
import com.dfsek.terra.addons.noise.config.templates.DomainWarpTemplate;
@@ -28,20 +27,17 @@ 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.GaborNoiseTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.PseudoErosionSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.SimpleNoiseTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.fractal.BrownianMotionTemplate;
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.normalizer.ClampNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.CubicSplineNormalizerTemplate;
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.NormalNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.PosterizationNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.ProbabilityNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.ScaleNormalizerTemplate;
import com.dfsek.terra.addons.noise.math.CubicSpline;
import com.dfsek.terra.addons.noise.samplers.arithmetic.AdditionSampler;
import com.dfsek.terra.addons.noise.samplers.arithmetic.DivisionSampler;
import com.dfsek.terra.addons.noise.samplers.arithmetic.MaxSampler;
@@ -94,8 +90,7 @@ public class NoiseAddon implements AddonInitializer {
.applyLoader(DistanceSampler.DistanceFunction.class,
(type, o, loader, depthTracker) -> DistanceSampler.DistanceFunction.valueOf((String) o))
.applyLoader(DimensionApplicableNoiseSampler.class, DimensionApplicableNoiseSampler::new)
.applyLoader(FunctionTemplate.class, FunctionTemplate::new)
.applyLoader(CubicSpline.Point.class, CubicSplinePointTemplate::new);
.applyLoader(FunctionTemplate.class, FunctionTemplate::new);
noiseRegistry.register(addon.key("LINEAR"), LinearNormalizerTemplate::new);
noiseRegistry.register(addon.key("NORMAL"), NormalNormalizerTemplate::new);
@@ -103,7 +98,6 @@ public class NoiseAddon implements AddonInitializer {
noiseRegistry.register(addon.key("PROBABILITY"), ProbabilityNormalizerTemplate::new);
noiseRegistry.register(addon.key("SCALE"), ScaleNormalizerTemplate::new);
noiseRegistry.register(addon.key("POSTERIZATION"), PosterizationNormalizerTemplate::new);
noiseRegistry.register(addon.key("CUBIC_SPLINE"), CubicSplineNormalizerTemplate::new);
noiseRegistry.register(addon.key("IMAGE"), ImageSamplerTemplate::new);
@@ -124,7 +118,6 @@ public class NoiseAddon implements AddonInitializer {
noiseRegistry.register(addon.key("VALUE_CUBIC"), () -> new SimpleNoiseTemplate(ValueCubicSampler::new));
noiseRegistry.register(addon.key("CELLULAR"), CellularNoiseTemplate::new);
noiseRegistry.register(addon.key("PSEUDOEROSION"), PseudoErosionSamplerTemplate::new);
noiseRegistry.register(addon.key("WHITE_NOISE"), () -> new SimpleNoiseTemplate(WhiteNoiseSampler::new));
noiseRegistry.register(addon.key("POSITIVE_WHITE_NOISE"), () -> new SimpleNoiseTemplate(PositiveWhiteNoiseSampler::new));
@@ -1,25 +0,0 @@
package com.dfsek.terra.addons.noise.config;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import com.dfsek.terra.addons.noise.math.CubicSpline.Point;
import com.dfsek.terra.api.config.meta.Meta;
public class CubicSplinePointTemplate implements ObjectTemplate<Point> {
@Value("from")
private @Meta double from;
@Value("to")
private @Meta double to;
@Value("gradient")
private @Meta double gradient;
@Override
public Point get() {
return new Point(from, to, gradient);
}
}
@@ -1,50 +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.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.random.WhiteNoiseSampler;
import com.dfsek.terra.addons.noise.samplers.noise.simplex.OpenSimplex2Sampler;
import com.dfsek.terra.addons.noise.samplers.noise.PseudoErosionSampler;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
@SuppressWarnings("FieldMayBeFinal")
public class PseudoErosionSamplerTemplate extends SamplerTemplate<PseudoErosionSampler> {
@Value("frequency")
@Default
protected @Meta double frequency = 0.01d;
// protected @Meta double frequency = 0.02d;
@Value("salt")
@Default
protected @Meta long salt = 0;
@Value("jitter")
@Default
private @Meta double jitter = 1.0D;
@Value("lookup")
@Default
private @Meta NoiseSampler lookup = null;
@Override
public NoiseSampler get() {
if(lookup == null) {
// OpenSimplex2Sampler l = new OpenSimplex2Sampler();
// l.setFrequency(0.0005);
lookup = new WhiteNoiseSampler();
}
return new PseudoErosionSampler(salt, frequency, lookup, jitter);
}
}
@@ -1,23 +0,0 @@
package com.dfsek.terra.addons.noise.config.templates.normalizer;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.math.CubicSpline;
import com.dfsek.terra.addons.noise.math.CubicSpline.Point;
import com.dfsek.terra.addons.noise.normalizer.CubicSplineNoiseSampler;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
import java.util.List;
public class CubicSplineNormalizerTemplate extends NormalizerTemplate<CubicSplineNoiseSampler> {
@Value("points")
private @Meta List<@Meta Point> points;
@Override
public NoiseSampler get() {
return new CubicSplineNoiseSampler(function, new CubicSpline(points));
}
}
@@ -1,89 +0,0 @@
package com.dfsek.terra.addons.noise.math;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
public class CubicSpline {
private final double[] fromValues;
private final double[] toValues;
private final double[] gradients;
public CubicSpline(List<Point> points) {
Collections.sort(points);
this.fromValues = new double[points.size()];
this.toValues = new double[points.size()];
this.gradients = new double[points.size()];
for(int i = 0; i < points.size(); i++) {
fromValues[i] = points.get(i).from;
toValues[i] = points.get(i).to;
gradients[i] = points.get(i).gradient;
}
}
public double apply(double in) {
return calculate(in, fromValues, toValues, gradients);
}
public static double calculate(double in, double[] fromValues, double[] toValues, double[] gradients) {
int pointIdx = floorBinarySearch(in, fromValues) - 1;
int pointIdxLast = fromValues.length - 1;
if (pointIdx < 0) { // If to left of first point return linear function intersecting said point using point's gradient
return gradients[0] * (in - fromValues[0]) + toValues[0];
} else if (pointIdx == pointIdxLast) { // Do same if to right of last point
return gradients[pointIdxLast] * (in - fromValues[pointIdxLast]) + toValues[pointIdxLast];
} else {
double fromLeft = fromValues[pointIdx];
double fromRight = fromValues[pointIdx + 1];
double toLeft = toValues[pointIdx];
double toRight = toValues[pointIdx + 1];
double gradientLeft = gradients[pointIdx];
double gradientRight = gradients[pointIdx + 1];
double fromDelta = fromRight - fromLeft;
double toDelta = toRight - toLeft;
double t = (in - fromLeft) / fromDelta;
return lerp(t, toLeft, toRight) + t * (1.0F - t) * lerp(t, gradientLeft * fromDelta - toDelta, -gradientRight * fromDelta + toDelta);
}
}
private static int floorBinarySearch(double targetValue, double[] values) {
int left = 0;
int right = values.length;
int idx = right - left;
while (idx > 0) {
int halfDelta = idx / 2;
int mid = left + halfDelta;
if (targetValue < values[mid]) {
idx = halfDelta;
} else {
left = mid + 1;
idx -= halfDelta + 1;
}
}
return left;
}
private static double lerp(double t, double a, double b) {
return a + t * (b - a);
}
public record Point(double from, double to, double gradient) implements Comparable<Point> {
@Override
public int compareTo(@NotNull CubicSpline.Point o) {
return Double.compare(from, o.from);
}
}
}
@@ -1,20 +0,0 @@
package com.dfsek.terra.addons.noise.normalizer;
import com.dfsek.terra.addons.noise.math.CubicSpline;
import com.dfsek.terra.api.noise.NoiseSampler;
public class CubicSplineNoiseSampler extends Normalizer {
private final CubicSpline spline;
public CubicSplineNoiseSampler(NoiseSampler sampler, CubicSpline spline) {
super(sampler);
this.spline = spline;
}
@Override
public double normalize(double in) {
return spline.apply(in);
}
}
@@ -240,37 +240,99 @@ public class CellularSampler extends NoiseFunction {
double centerX = x;
double centerY = y;
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int hash = hash(seed, xPrimed, yPrimed);
int idx = hash & (255 << 1);
double vecX = (xi - x) + RAND_VECS_2D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_2D[idx | 1] * cellularJitter;
double newDistance = switch(distanceFunction) {
case Manhattan -> fastAbs(vecX) + fastAbs(vecY);
case Hybrid -> (fastAbs(vecX) + fastAbs(vecY)) + (vecX * vecX + vecY * vecY);
default -> vecX * vecX + vecY * vecY;
};
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_2D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
switch(distanceFunction) {
default:
case Euclidean:
case EuclideanSq:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int hash = hash(seed, xPrimed, yPrimed);
int idx = hash & (255 << 1);
double vecX = (xi - x) + RAND_VECS_2D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_2D[idx | 1] * cellularJitter;
double newDistance = vecX * vecX + vecY * vecY;
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_2D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
break;
case Manhattan:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int hash = hash(seed, xPrimed, yPrimed);
int idx = hash & (255 << 1);
double vecX = (xi - x) + RAND_VECS_2D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_2D[idx | 1] * cellularJitter;
double newDistance = fastAbs(vecX) + fastAbs(vecY);
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_2D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
case Hybrid:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int hash = hash(seed, xPrimed, yPrimed);
int idx = hash & (255 << 1);
double vecX = (xi - x) + RAND_VECS_2D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_2D[idx | 1] * cellularJitter;
double newDistance = (fastAbs(vecX) + fastAbs(vecY)) + (vecX * vecX + vecY * vecY);
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_2D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_2D[idx | 1] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
}
if(distanceFunction == DistanceFunction.Euclidean && returnType != ReturnType.CellValue) {
@@ -289,7 +351,6 @@ public class CellularSampler extends NoiseFunction {
case Distance2Mul -> distance1 * distance0 * 0.5 - 1;
case Distance2Div -> distance0 / distance1 - 1;
case NoiseLookup -> noiseLookup.noise(sl, centerX, centerY);
case LocalNoiseLookup -> noiseLookup.noise(sl, x / frequency - centerX, y / frequency - centerY);
case Distance3 -> distance2 - 1;
case Distance3Add -> (distance2 + distance0) * 0.5 - 1;
case Distance3Sub -> distance2 - distance0 - 1;
@@ -321,47 +382,120 @@ public class CellularSampler extends NoiseFunction {
double centerY = y;
double centerZ = z;
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int zPrimed = zPrimedBase;
for(int zi = zr - 1; zi <= zr + 1; zi++) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
int idx = hash & (255 << 2);
switch(distanceFunction) {
case Euclidean:
case EuclideanSq:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
double vecX = (xi - x) + RAND_VECS_3D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_3D[idx | 1] * cellularJitter;
double vecZ = (zi - z) + RAND_VECS_3D[idx | 2] * cellularJitter;
double newDistance = 0;
switch(distanceFunction) {
case Euclidean, EuclideanSq -> newDistance = vecX * vecX + vecY * vecY + vecZ * vecZ;
case Manhattan -> newDistance = fastAbs(vecX) + fastAbs(vecY) + fastAbs(vecZ);
case Hybrid -> {
newDistance = (fastAbs(vecX) + fastAbs(vecY) + fastAbs(vecZ)) + (vecX * vecX + vecY * vecY + vecZ * vecZ);
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int zPrimed = zPrimedBase;
for(int zi = zr - 1; zi <= zr + 1; zi++) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
int idx = hash & (255 << 2);
double vecX = (xi - x) + RAND_VECS_3D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_3D[idx | 1] * cellularJitter;
double vecZ = (zi - z) + RAND_VECS_3D[idx | 2] * cellularJitter;
double newDistance = vecX * vecX + vecY * vecY + vecZ * vecZ;
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_3D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / frequency);
centerZ = ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
}
yPrimed += PRIME_Y;
}
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_3D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / frequency);
centerZ = ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
xPrimed += PRIME_X;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
break;
case Manhattan:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int zPrimed = zPrimedBase;
for(int zi = zr - 1; zi <= zr + 1; zi++) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
int idx = hash & (255 << 2);
double vecX = (xi - x) + RAND_VECS_3D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_3D[idx | 1] * cellularJitter;
double vecZ = (zi - z) + RAND_VECS_3D[idx | 2] * cellularJitter;
double newDistance = fastAbs(vecX) + fastAbs(vecY) + fastAbs(vecZ);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_3D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / frequency);
centerZ = ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
case Hybrid:
for(int xi = xr - 1; xi <= xr + 1; xi++) {
int yPrimed = yPrimedBase;
for(int yi = yr - 1; yi <= yr + 1; yi++) {
int zPrimed = zPrimedBase;
for(int zi = zr - 1; zi <= zr + 1; zi++) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
int idx = hash & (255 << 2);
double vecX = (xi - x) + RAND_VECS_3D[idx] * cellularJitter;
double vecY = (yi - y) + RAND_VECS_3D[idx | 1] * cellularJitter;
double vecZ = (zi - z) + RAND_VECS_3D[idx | 2] * cellularJitter;
double newDistance = (fastAbs(vecX) + fastAbs(vecY) + fastAbs(vecZ)) +
(vecX * vecX + vecY * vecY + vecZ * vecZ);
distance1 = fastMax(fastMin(distance1, newDistance), distance0);
if(newDistance < distance0) {
distance0 = newDistance;
closestHash = hash;
centerX = ((xi + RAND_VECS_3D[idx] * cellularJitter) / frequency);
centerY = ((yi + RAND_VECS_3D[idx | 1] * cellularJitter) / frequency);
centerZ = ((zi + RAND_VECS_3D[idx | 2] * cellularJitter) / frequency);
} else if(newDistance < distance1) {
distance2 = distance1;
distance1 = newDistance;
} else if(newDistance < distance2) {
distance2 = newDistance;
}
zPrimed += PRIME_Z;
}
yPrimed += PRIME_Y;
}
xPrimed += PRIME_X;
}
break;
default:
break;
}
if(distanceFunction == DistanceFunction.Euclidean && returnType != ReturnType.CellValue) {
@@ -380,7 +514,6 @@ public class CellularSampler extends NoiseFunction {
case Distance2Mul -> distance1 * distance0 * 0.5 - 1;
case Distance2Div -> distance0 / distance1 - 1;
case NoiseLookup -> noiseLookup.noise(sl, centerX, centerY, centerZ);
case LocalNoiseLookup -> noiseLookup.noise(sl, x / frequency - centerX, y / frequency - centerY, z / frequency - centerZ);
case Distance3 -> distance2 - 1;
case Distance3Add -> (distance2 + distance0) * 0.5 - 1;
case Distance3Sub -> distance2 - distance0 - 1;
@@ -407,7 +540,6 @@ public class CellularSampler extends NoiseFunction {
Distance2Mul,
Distance2Div,
NoiseLookup,
LocalNoiseLookup,
Distance3,
Distance3Add,
Distance3Sub,
@@ -1,384 +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.noise.samplers.noise;
import com.dfsek.terra.addons.noise.samplers.noise.PseudoErosionSampler.CellChunk2D.ChunkPos;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import net.jafama.FastMath;
import static com.dfsek.terra.addons.noise.samplers.noise.NoiseFunction.PRIME_X;
import static com.dfsek.terra.addons.noise.samplers.noise.NoiseFunction.PRIME_Y;
import static com.dfsek.terra.addons.noise.samplers.noise.NoiseFunction.fastAbs;
import static com.dfsek.terra.addons.noise.samplers.noise.NoiseFunction.fastMin;
import static com.dfsek.terra.addons.noise.samplers.noise.NoiseFunction.fastRound;
import static com.dfsek.terra.addons.noise.samplers.noise.NoiseFunction.fastSqrt;
import static com.dfsek.terra.addons.noise.samplers.noise.NoiseFunction.hash;
import static net.jafama.FastMath.pow2;
import static net.jafama.FastMath.round;
/**
* Pseudo-erosion algorithm based on <a href="https://www.reddit.com/r/proceduralgeneration/comments/797fgw/iterative_pseudoerosion/">a reddit post</a>
* by user /u/YankeeMinstrel.
* <br>
* The algorithm works similarly to cellular/worley/voronoi noise. A grid of cells is established, where each cell contains a position with
* a random offset. Each cell connects to an adjacent candidate cell in its moore neighbourhood or itself, called the 'connected' cell.
* The connected cell is chosen by determining which candidate cell has the lowest value provided by passing the candidate coordinates into
* another 'lookup' noise function.
* The algorithm iterates through the cells near the sample point, calculates the distance between the position and the line segment
* between the cell and its connected cell, and returns the minimum of these distances.
*/
public class PseudoErosionSampler implements NoiseSampler {
private static final double[] RAND_VECS_3D = {
-0.7292736885d, -0.6618439697d, 0.1735581948d, 0, 0.790292081d, -0.5480887466d, -0.2739291014d, 0, 0.7217578935d, 0.6226212466d,
-0.3023380997d, 0, 0.565683137d, -0.8208298145d, -0.0790000257d, 0, 0.760049034d, -0.5555979497d, -0.3370999617d, 0,
0.3713945616d, 0.5011264475d, 0.7816254623d, 0, -0.1277062463d, -0.4254438999d, -0.8959289049d, 0, -0.2881560924d,
-0.5815838982d, 0.7607405838d, 0, 0.5849561111d, -0.662820239d, -0.4674352136d, 0, 0.3307171178d, 0.0391653737d, 0.94291689d, 0,
0.8712121778d, -0.4113374369d, -0.2679381538d, 0, 0.580981015d, 0.7021915846d, 0.4115677815d, 0, 0.503756873d, 0.6330056931d,
-0.5878203852d, 0, 0.4493712205d, 0.601390195d, 0.6606022552d, 0, -0.6878403724d, 0.09018890807d, -0.7202371714d, 0,
-0.5958956522d, -0.6469350577d, 0.475797649d, 0, -0.5127052122d, 0.1946921978d, -0.8361987284d, 0, -0.9911507142d,
-0.05410276466d, -0.1212153153d, 0, -0.2149721042d, 0.9720882117d, -0.09397607749d, 0, -0.7518650936d, -0.5428057603d,
0.3742469607d, 0, 0.5237068895d, 0.8516377189d, -0.02107817834d, 0, 0.6333504779d, 0.1926167129d, -0.7495104896d, 0,
-0.06788241606d, 0.3998305789d, 0.9140719259d, 0, -0.5538628599d, -0.4729896695d, -0.6852128902d, 0, -0.7261455366d,
-0.5911990757d, 0.3509933228d, 0, -0.9229274737d, -0.1782808786d, 0.3412049336d, 0, -0.6968815002d, 0.6511274338d,
0.3006480328d, 0, 0.9608044783d, -0.2098363234d, -0.1811724921d, 0, 0.06817146062d, -0.9743405129d, 0.2145069156d, 0,
-0.3577285196d, -0.6697087264d, -0.6507845481d, 0, -0.1868621131d, 0.7648617052d, -0.6164974636d, 0, -0.6541697588d,
0.3967914832d, 0.6439087246d, 0, 0.6993340405d, -0.6164538506d, 0.3618239211d, 0, -0.1546665739d, 0.6291283928d, 0.7617583057d,
0, -0.6841612949d, -0.2580482182d, -0.6821542638d, 0, 0.5383980957d, 0.4258654885d, 0.7271630328d, 0, -0.5026987823d,
-0.7939832935d, -0.3418836993d, 0, 0.3202971715d, 0.2834415347d, 0.9039195862d, 0, 0.8683227101d, -0.0003762656404d,
-0.4959995258d, 0, 0.791120031d, -0.08511045745d, 0.6057105799d, 0, -0.04011016052d, -0.4397248749d, 0.8972364289d, 0,
0.9145119872d, 0.3579346169d, -0.1885487608d, 0, -0.9612039066d, -0.2756484276d, 0.01024666929d, 0, 0.6510361721d,
-0.2877799159d, -0.7023778346d, 0, -0.2041786351d, 0.7365237271d, 0.644859585d, 0, -0.7718263711d, 0.3790626912d, 0.5104855816d,
0, -0.3060082741d, -0.7692987727d, 0.5608371729d, 0, 0.454007341d, -0.5024843065d, 0.7357899537d, 0, 0.4816795475d,
0.6021208291d, -0.6367380315d, 0, 0.6961980369d, -0.3222197429d, 0.641469197d, 0, -0.6532160499d, -0.6781148932d, 0.3368515753d,
0, 0.5089301236d, -0.6154662304d, -0.6018234363d, 0, -0.1635919754d, -0.9133604627d, -0.372840892d, 0, 0.52408019d,
-0.8437664109d, 0.1157505864d, 0, 0.5902587356d, 0.4983817807d, -0.6349883666d, 0, 0.5863227872d, 0.494764745d, 0.6414307729d,
0, 0.6779335087d, 0.2341345225d, 0.6968408593d, 0, 0.7177054546d, -0.6858979348d, 0.120178631d, 0, -0.5328819713d,
-0.5205125012d, 0.6671608058d, 0, -0.8654874251d, -0.0700727088d, -0.4960053754d, 0, -0.2861810166d, 0.7952089234d,
0.5345495242d, 0, -0.04849529634d, 0.9810836427d, -0.1874115585d, 0, -0.6358521667d, 0.6058348682d, 0.4781800233d, 0,
0.6254794696d, -0.2861619734d, 0.7258696564d, 0, -0.2585259868d, 0.5061949264d, -0.8227581726d, 0, 0.02136306781d,
0.5064016808d, -0.8620330371d, 0, 0.200111773d, 0.8599263484d, 0.4695550591d, 0, 0.4743561372d, 0.6014985084d, -0.6427953014d,
0, 0.6622993731d, -0.5202474575d, -0.5391679918d, 0, 0.08084972818d, -0.6532720452d, 0.7527940996d, 0, -0.6893687501d,
0.0592860349d, 0.7219805347d, 0, -0.1121887082d, -0.9673185067d, 0.2273952515d, 0, 0.7344116094d, 0.5979668656d, -0.3210532909d,
0, 0.5789393465d, -0.2488849713d, 0.7764570201d, 0, 0.6988182827d, 0.3557169806d, -0.6205791146d, 0, -0.8636845529d,
-0.2748771249d, -0.4224826141d, 0, -0.4247027957d, -0.4640880967d, 0.777335046d, 0, 0.5257722489d, -0.8427017621d,
0.1158329937d, 0, 0.9343830603d, 0.316302472d, -0.1639543925d, 0, -0.1016836419d, -0.8057303073d, -0.5834887393d, 0,
-0.6529238969d, 0.50602126d, -0.5635892736d, 0, -0.2465286165d, -0.9668205684d, -0.06694497494d, 0, -0.9776897119d,
-0.2099250524d, -0.007368825344d, 0, 0.7736893337d, 0.5734244712d, 0.2694238123d, 0, -0.6095087895d, 0.4995678998d,
0.6155736747d, 0, 0.5794535482d, 0.7434546771d, 0.3339292269d, 0, -0.8226211154d, 0.08142581855d, 0.5627293636d, 0,
-0.510385483d, 0.4703667658d, 0.7199039967d, 0, -0.5764971849d, -0.07231656274d, -0.8138926898d, 0, 0.7250628871d,
0.3949971505d, -0.5641463116d, 0, -0.1525424005d, 0.4860840828d, -0.8604958341d, 0, -0.5550976208d, -0.4957820792d,
0.667882296d, 0, -0.1883614327d, 0.9145869398d, 0.357841725d, 0, 0.7625556724d, -0.5414408243d, -0.3540489801d, 0,
-0.5870231946d, -0.3226498013d, -0.7424963803d, 0, 0.3051124198d, 0.2262544068d, -0.9250488391d, 0, 0.6379576059d, 0.577242424d,
-0.5097070502d, 0, -0.5966775796d, 0.1454852398d, -0.7891830656d, 0, -0.658330573d, 0.6555487542d, -0.3699414651d, 0,
0.7434892426d, 0.2351084581d, 0.6260573129d, 0, 0.5562114096d, 0.8264360377d, -0.0873632843d, 0, -0.3028940016d, -0.8251527185d,
0.4768419182d, 0, 0.1129343818d, -0.985888439d, -0.1235710781d, 0, 0.5937652891d, -0.5896813806d, 0.5474656618d, 0,
0.6757964092d, -0.5835758614d, -0.4502648413d, 0, 0.7242302609d, -0.1152719764d, 0.6798550586d, 0, -0.9511914166d,
0.0753623979d, -0.2992580792d, 0, 0.2539470961d, -0.1886339355d, 0.9486454084d, 0, 0.571433621d, -0.1679450851d, -0.8032795685d,
0, -0.06778234979d, 0.3978269256d, 0.9149531629d, 0, 0.6074972649d, 0.733060024d, -0.3058922593d, 0, -0.5435478392d,
0.1675822484d, 0.8224791405d, 0, -0.5876678086d, -0.3380045064d, -0.7351186982d, 0, -0.7967562402d, 0.04097822706d,
-0.6029098428d, 0, -0.1996350917d, 0.8706294745d, 0.4496111079d, 0, -0.02787660336d, -0.9106232682d, -0.4122962022d, 0,
-0.7797625996d, -0.6257634692d, 0.01975775581d, 0, -0.5211232846d, 0.7401644346d, -0.4249554471d, 0, 0.8575424857d,
0.4053272873d, -0.3167501783d, 0, 0.1045223322d, 0.8390195772d, -0.5339674439d, 0, 0.3501822831d, 0.9242524096d, -0.1520850155d,
0, 0.1987849858d, 0.07647613266d, 0.9770547224d, 0, 0.7845996363d, 0.6066256811d, -0.1280964233d, 0, 0.09006737436d,
-0.9750989929d, -0.2026569073d, 0, -0.8274343547d, -0.542299559d, 0.1458203587d, 0, -0.3485797732d, -0.415802277d, 0.840000362d,
0, -0.2471778936d, -0.7304819962d, -0.6366310879d, 0, -0.3700154943d, 0.8577948156d, 0.3567584454d, 0, 0.5913394901d,
-0.548311967d, -0.5913303597d, 0, 0.1204873514d, -0.7626472379d, -0.6354935001d, 0, 0.616959265d, 0.03079647928d, 0.7863922953d,
0, 0.1258156836d, -0.6640829889d, -0.7369967419d, 0, -0.6477565124d, -0.1740147258d, -0.7417077429d, 0, 0.6217889313d,
-0.7804430448d, -0.06547655076d, 0, 0.6589943422d, -0.6096987708d, 0.4404473475d, 0, -0.2689837504d, -0.6732403169d,
-0.6887635427d, 0, -0.3849775103d, 0.5676542638d, 0.7277093879d, 0, 0.5754444408d, 0.8110471154d, -0.1051963504d, 0,
0.9141593684d, 0.3832947817d, 0.131900567d, 0, -0.107925319d, 0.9245493968d, 0.3654593525d, 0, 0.377977089d, 0.3043148782d,
0.8743716458d, 0, -0.2142885215d, -0.8259286236d, 0.5214617324d, 0, 0.5802544474d, 0.4148098596d, -0.7008834116d, 0,
-0.1982660881d, 0.8567161266d, -0.4761596756d, 0, -0.03381553704d, 0.3773180787d, -0.9254661404d, 0, -0.6867922841d,
-0.6656597827d, 0.2919133642d, 0, 0.7731742607d, -0.2875793547d, -0.5652430251d, 0, -0.09655941928d, 0.9193708367d,
-0.3813575004d, 0, 0.2715702457d, -0.9577909544d, -0.09426605581d, 0, 0.2451015704d, -0.6917998565d, -0.6792188003d, 0,
0.977700782d, -0.1753855374d, 0.1155036542d, 0, -0.5224739938d, 0.8521606816d, 0.02903615945d, 0, -0.7734880599d,
-0.5261292347d, 0.3534179531d, 0, -0.7134492443d, -0.269547243d, 0.6467878011d, 0, 0.1644037271d, 0.5105846203d, -0.8439637196d,
0, 0.6494635788d, 0.05585611296d, 0.7583384168d, 0, -0.4711970882d, 0.5017280509d, -0.7254255765d, 0, -0.6335764307d,
-0.2381686273d, -0.7361091029d, 0, -0.9021533097d, -0.270947803d, -0.3357181763d, 0, -0.3793711033d, 0.872258117d,
0.3086152025d, 0, -0.6855598966d, -0.3250143309d, 0.6514394162d, 0, 0.2900942212d, -0.7799057743d, -0.5546100667d, 0,
-0.2098319339d, 0.85037073d, 0.4825351604d, 0, -0.4592603758d, 0.6598504336d, -0.5947077538d, 0, 0.8715945488d, 0.09616365406d,
-0.4807031248d, 0, -0.6776666319d, 0.7118504878d, -0.1844907016d, 0, 0.7044377633d, 0.312427597d, 0.637304036d, 0,
-0.7052318886d, -0.2401093292d, -0.6670798253d, 0, 0.081921007d, -0.7207336136d, -0.6883545647d, 0, -0.6993680906d,
-0.5875763221d, -0.4069869034d, 0, -0.1281454481d, 0.6419895885d, 0.7559286424d, 0, -0.6337388239d, -0.6785471501d,
-0.3714146849d, 0, 0.5565051903d, -0.2168887573d, -0.8020356851d, 0, -0.5791554484d, 0.7244372011d, -0.3738578718d, 0,
0.1175779076d, -0.7096451073d, 0.6946792478d, 0, -0.6134619607d, 0.1323631078d, 0.7785527795d, 0, 0.6984635305d,
-0.02980516237d, -0.715024719d, 0, 0.8318082963d, -0.3930171956d, 0.3919597455d, 0, 0.1469576422d, 0.05541651717d,
-0.9875892167d, 0, 0.708868575d, -0.2690503865d, 0.6520101478d, 0, 0.2726053183d, 0.67369766d, -0.68688995d, 0, -0.6591295371d,
0.3035458599d, -0.6880466294d, 0, 0.4815131379d, -0.7528270071d, 0.4487723203d, 0, 0.9430009463d, 0.1675647412d, -0.2875261255d,
0, 0.434802957d, 0.7695304522d, -0.4677277752d, 0, 0.3931996188d, 0.594473625d, 0.7014236729d, 0, 0.7254336655d, -0.603925654d,
0.3301814672d, 0, 0.7590235227d, -0.6506083235d, 0.02433313207d, 0, -0.8552768592d, -0.3430042733d, 0.3883935666d, 0,
-0.6139746835d, 0.6981725247d, 0.3682257648d, 0, -0.7465905486d, -0.5752009504d, 0.3342849376d, 0, 0.5730065677d, 0.810555537d,
-0.1210916791d, 0, -0.9225877367d, -0.3475211012d, -0.167514036d, 0, -0.7105816789d, -0.4719692027d, -0.5218416899d, 0,
-0.08564609717d, 0.3583001386d, 0.929669703d, 0, -0.8279697606d, -0.2043157126d, 0.5222271202d, 0, 0.427944023d, 0.278165994d,
0.8599346446d, 0, 0.5399079671d, -0.7857120652d, -0.3019204161d, 0, 0.5678404253d, -0.5495413974d, -0.6128307303d, 0,
-0.9896071041d, 0.1365639107d, -0.04503418428d, 0, -0.6154342638d, -0.6440875597d, 0.4543037336d, 0, 0.1074204368d,
-0.7946340692d, 0.5975094525d, 0, -0.3595449969d, -0.8885529948d, 0.28495784d, 0, -0.2180405296d, 0.1529888965d, 0.9638738118d,
0, -0.7277432317d, -0.6164050508d, -0.3007234646d, 0, 0.7249729114d, -0.00669719484d, 0.6887448187d, 0, -0.5553659455d,
-0.5336586252d, 0.6377908264d, 0, 0.5137558015d, 0.7976208196d, -0.3160000073d, 0, -0.3794024848d, 0.9245608561d,
-0.03522751494d, 0, 0.8229248658d, 0.2745365933d, -0.4974176556d, 0, -0.5404114394d, 0.6091141441d, 0.5804613989d, 0,
0.8036581901d, -0.2703029469d, 0.5301601931d, 0, 0.6044318879d, 0.6832968393d, 0.4095943388d, 0, 0.06389988817d, 0.9658208605d,
-0.2512108074d, 0, 0.1087113286d, 0.7402471173d, -0.6634877936d, 0, -0.713427712d, -0.6926784018d, 0.1059128479d, 0,
0.6458897819d, -0.5724548511d, -0.5050958653d, 0, -0.6553931414d, 0.7381471625d, 0.159995615d, 0, 0.3910961323d, 0.9188871375d,
-0.05186755998d, 0, -0.4879022471d, -0.5904376907d, 0.6429111375d, 0, 0.6014790094d, 0.7707441366d, -0.2101820095d, 0,
-0.5677173047d, 0.7511360995d, 0.3368851762d, 0, 0.7858573506d, 0.226674665d, 0.5753666838d, 0, -0.4520345543d, -0.604222686d,
-0.6561857263d, 0, 0.002272116345d, 0.4132844051d, -0.9105991643d, 0, -0.5815751419d, -0.5162925989d, 0.6286591339d, 0,
-0.03703704785d, 0.8273785755d, 0.5604221175d, 0, -0.5119692504d, 0.7953543429d, -0.3244980058d, 0, -0.2682417366d,
-0.9572290247d, -0.1084387619d, 0, -0.2322482736d, -0.9679131102d, -0.09594243324d, 0, 0.3554328906d, -0.8881505545d,
0.2913006227d, 0, 0.7346520519d, -0.4371373164d, 0.5188422971d, 0, 0.9985120116d, 0.04659011161d, -0.02833944577d, 0,
-0.3727687496d, -0.9082481361d, 0.1900757285d, 0, 0.91737377d, -0.3483642108d, 0.1925298489d, 0, 0.2714911074d, 0.4147529736d,
-0.8684886582d, 0, 0.5131763485d, -0.7116334161d, 0.4798207128d, 0, -0.8737353606d, 0.18886992d, -0.4482350644d, 0,
0.8460043821d, -0.3725217914d, 0.3814499973d, 0, 0.8978727456d, -0.1780209141d, -0.4026575304d, 0, 0.2178065647d,
-0.9698322841d, -0.1094789531d, 0, -0.1518031304d, -0.7788918132d, -0.6085091231d, 0, -0.2600384876d, -0.4755398075d,
-0.8403819825d, 0, 0.572313509d, -0.7474340931d, -0.3373418503d, 0, -0.7174141009d, 0.1699017182d, -0.6756111411d, 0,
-0.684180784d, 0.02145707593d, -0.7289967412d, 0, -0.2007447902d, 0.06555605789d, -0.9774476623d, 0, -0.1148803697d,
-0.8044887315d, 0.5827524187d, 0, -0.7870349638d, 0.03447489231d, 0.6159443543d, 0, -0.2015596421d, 0.6859872284d,
0.6991389226d, 0, -0.08581082512d, -0.10920836d, -0.9903080513d, 0, 0.5532693395d, 0.7325250401d, -0.396610771d, 0,
-0.1842489331d, -0.9777375055d, -0.1004076743d, 0, 0.0775473789d, -0.9111505856d, 0.4047110257d, 0, 0.1399838409d,
0.7601631212d, -0.6344734459d, 0, 0.4484419361d, -0.845289248d, 0.2904925424d, 0
};
private static final double[] RAND_VECS_2D = {
-0.2700222198d, -0.9628540911d, 0.3863092627d, -0.9223693152d, 0.04444859006d, -0.999011673d, -0.5992523158d, -0.8005602176d,
-0.7819280288d, 0.6233687174d, 0.9464672271d, 0.3227999196d, -0.6514146797d, -0.7587218957d, 0.9378472289d, 0.347048376d,
-0.8497875957d, -0.5271252623d, -0.879042592d, 0.4767432447d, -0.892300288d, -0.4514423508d, -0.379844434d, -0.9250503802d,
-0.9951650832d, 0.0982163789d, 0.7724397808d, -0.6350880136d, 0.7573283322d, -0.6530343002d, -0.9928004525d, -0.119780055d,
-0.0532665713d, 0.9985803285d, 0.9754253726d, -0.2203300762d, -0.7665018163d, 0.6422421394d, 0.991636706d, 0.1290606184d,
-0.994696838d, 0.1028503788d, -0.5379205513d, -0.84299554d, 0.5022815471d, -0.8647041387d, 0.4559821461d, -0.8899889226d,
-0.8659131224d, -0.5001944266d, 0.0879458407d, -0.9961252577d, -0.5051684983d, 0.8630207346d, 0.7753185226d, -0.6315704146d,
-0.6921944612d, 0.7217110418d, -0.5191659449d, -0.8546734591d, 0.8978622882d, -0.4402764035d, -0.1706774107d, 0.9853269617d,
-0.9353430106d, -0.3537420705d, -0.9992404798d, 0.03896746794d, -0.2882064021d, -0.9575683108d, -0.9663811329d, 0.2571137995d,
-0.8759714238d, -0.4823630009d, -0.8303123018d, -0.5572983775d, 0.05110133755d, -0.9986934731d, -0.8558373281d, -0.5172450752d,
0.09887025282d, 0.9951003332d, 0.9189016087d, 0.3944867976d, -0.2439375892d, -0.9697909324d, -0.8121409387d, -0.5834613061d,
-0.9910431363d, 0.1335421355d, 0.8492423985d, -0.5280031709d, -0.9717838994d, -0.2358729591d, 0.9949457207d, 0.1004142068d,
0.6241065508d, -0.7813392434d, 0.662910307d, 0.7486988212d, -0.7197418176d, 0.6942418282d, -0.8143370775d, -0.5803922158d,
0.104521054d, -0.9945226741d, -0.1065926113d, -0.9943027784d, 0.445799684d, -0.8951327509d, 0.105547406d, 0.9944142724d,
-0.992790267d, 0.1198644477d, -0.8334366408d, 0.552615025d, 0.9115561563d, -0.4111755999d, 0.8285544909d, -0.5599084351d,
0.7217097654d, -0.6921957921d, 0.4940492677d, -0.8694339084d, -0.3652321272d, -0.9309164803d, -0.9696606758d, 0.2444548501d,
0.08925509731d, -0.996008799d, 0.5354071276d, -0.8445941083d, -0.1053576186d, 0.9944343981d, -0.9890284586d, 0.1477251101d,
0.004856104961d, 0.9999882091d, 0.9885598478d, 0.1508291331d, 0.9286129562d, -0.3710498316d, -0.5832393863d, -0.8123003252d,
0.3015207509d, 0.9534596146d, -0.9575110528d, 0.2883965738d, 0.9715802154d, -0.2367105511d, 0.229981792d, 0.9731949318d,
0.955763816d, -0.2941352207d, 0.740956116d, 0.6715534485d, -0.9971513787d, -0.07542630764d, 0.6905710663d, -0.7232645452d,
-0.290713703d, -0.9568100872d, 0.5912777791d, -0.8064679708d, -0.9454592212d, -0.325740481d, 0.6664455681d, 0.74555369d,
0.6236134912d, 0.7817328275d, 0.9126993851d, -0.4086316587d, -0.8191762011d, 0.5735419353d, -0.8812745759d, -0.4726046147d,
0.9953313627d, 0.09651672651d, 0.9855650846d, -0.1692969699d, -0.8495980887d, 0.5274306472d, 0.6174853946d, -0.7865823463d,
0.8508156371d, 0.52546432d, 0.9985032451d, -0.05469249926d, 0.1971371563d, -0.9803759185d, 0.6607855748d, -0.7505747292d,
-0.03097494063d, 0.9995201614d, -0.6731660801d, 0.739491331d, -0.7195018362d, -0.6944905383d, 0.9727511689d, 0.2318515979d,
0.9997059088d, -0.0242506907d, 0.4421787429d, -0.8969269532d, 0.9981350961d, -0.061043673d, -0.9173660799d, -0.3980445648d,
-0.8150056635d, -0.5794529907d, -0.8789331304d, 0.4769450202d, 0.0158605829d, 0.999874213d, -0.8095464474d, 0.5870558317d,
-0.9165898907d, -0.3998286786d, -0.8023542565d, 0.5968480938d, -0.5176737917d, 0.8555780767d, -0.8154407307d, -0.5788405779d,
0.4022010347d, -0.9155513791d, -0.9052556868d, -0.4248672045d, 0.7317445619d, 0.6815789728d, -0.5647632201d, -0.8252529947d,
-0.8403276335d, -0.5420788397d, -0.9314281527d, 0.363925262d, 0.5238198472d, 0.8518290719d, 0.7432803869d, -0.6689800195d,
-0.985371561d, -0.1704197369d, 0.4601468731d, 0.88784281d, 0.825855404d, 0.5638819483d, 0.6182366099d, 0.7859920446d,
0.8331502863d, -0.553046653d, 0.1500307506d, 0.9886813308d, -0.662330369d, -0.7492119075d, -0.668598664d, 0.743623444d,
0.7025606278d, 0.7116238924d, -0.5419389763d, -0.8404178401d, -0.3388616456d, 0.9408362159d, 0.8331530315d, 0.5530425174d,
-0.2989720662d, -0.9542618632d, 0.2638522993d, 0.9645630949d, 0.124108739d, -0.9922686234d, -0.7282649308d, -0.6852956957d,
0.6962500149d, 0.7177993569d, -0.9183535368d, 0.3957610156d, -0.6326102274d, -0.7744703352d, -0.9331891859d, -0.359385508d,
-0.1153779357d, -0.9933216659d, 0.9514974788d, -0.3076565421d, -0.08987977445d, -0.9959526224d, 0.6678496916d, 0.7442961705d,
0.7952400393d, -0.6062947138d, -0.6462007402d, -0.7631674805d, -0.2733598753d, 0.9619118351d, 0.9669590226d, -0.254931851d,
-0.9792894595d, 0.2024651934d, -0.5369502995d, -0.8436138784d, -0.270036471d, -0.9628500944d, -0.6400277131d, 0.7683518247d,
-0.7854537493d, -0.6189203566d, 0.06005905383d, -0.9981948257d, -0.02455770378d, 0.9996984141d, -0.65983623d, 0.751409442d,
-0.6253894466d, -0.7803127835d, -0.6210408851d, -0.7837781695d, 0.8348888491d, 0.5504185768d, -0.1592275245d, 0.9872419133d,
0.8367622488d, 0.5475663786d, -0.8675753916d, -0.4973056806d, -0.2022662628d, -0.9793305667d, 0.9399189937d, 0.3413975472d,
0.9877404807d, -0.1561049093d, -0.9034455656d, 0.4287028224d, 0.1269804218d, -0.9919052235d, -0.3819600854d, 0.924178821d,
0.9754625894d, 0.2201652486d, -0.3204015856d, -0.9472818081d, -0.9874760884d, 0.1577687387d, 0.02535348474d, -0.9996785487d,
0.4835130794d, -0.8753371362d, -0.2850799925d, -0.9585037287d, -0.06805516006d, -0.99768156d, -0.7885244045d, -0.6150034663d,
0.3185392127d, -0.9479096845d, 0.8880043089d, 0.4598351306d, 0.6476921488d, -0.7619021462d, 0.9820241299d, 0.1887554194d,
0.9357275128d, -0.3527237187d, -0.8894895414d, 0.4569555293d, 0.7922791302d, 0.6101588153d, 0.7483818261d, 0.6632681526d,
-0.7288929755d, -0.6846276581d, 0.8729032783d, -0.4878932944d, 0.8288345784d, 0.5594937369d, 0.08074567077d, 0.9967347374d,
0.9799148216d, -0.1994165048d, -0.580730673d, -0.8140957471d, -0.4700049791d, -0.8826637636d, 0.2409492979d, 0.9705377045d,
0.9437816757d, -0.3305694308d, -0.8927998638d, -0.4504535528d, -0.8069622304d, 0.5906030467d, 0.06258973166d, 0.9980393407d,
-0.9312597469d, 0.3643559849d, 0.5777449785d, 0.8162173362d, -0.3360095855d, -0.941858566d, 0.697932075d, -0.7161639607d,
-0.002008157227d, -0.9999979837d, -0.1827294312d, -0.9831632392d, -0.6523911722d, 0.7578824173d, -0.4302626911d, -0.9027037258d,
-0.9985126289d, -0.05452091251d, -0.01028102172d, -0.9999471489d, -0.4946071129d, 0.8691166802d, -0.2999350194d, 0.9539596344d,
0.8165471961d, 0.5772786819d, 0.2697460475d, 0.962931498d, -0.7306287391d, -0.6827749597d, -0.7590952064d, -0.6509796216d,
-0.907053853d, 0.4210146171d, -0.5104861064d, -0.8598860013d, 0.8613350597d, 0.5080373165d, 0.5007881595d, -0.8655698812d,
-0.654158152d, 0.7563577938d, -0.8382755311d, -0.545246856d, 0.6940070834d, 0.7199681717d, 0.06950936031d, 0.9975812994d,
0.1702942185d, -0.9853932612d, 0.2695973274d, 0.9629731466d, 0.5519612192d, -0.8338697815d, 0.225657487d, -0.9742067022d,
0.4215262855d, -0.9068161835d, 0.4881873305d, -0.8727388672d, -0.3683854996d, -0.9296731273d, -0.9825390578d, 0.1860564427d,
0.81256471d, 0.5828709909d, 0.3196460933d, -0.9475370046d, 0.9570913859d, 0.2897862643d, -0.6876655497d, -0.7260276109d,
-0.9988770922d, -0.047376731d, -0.1250179027d, 0.992154486d, -0.8280133617d, 0.560708367d, 0.9324863769d, -0.3612051451d,
0.6394653183d, 0.7688199442d, -0.01623847064d, -0.9998681473d, -0.9955014666d, -0.09474613458d, -0.81453315d, 0.580117012d,
0.4037327978d, -0.9148769469d, 0.9944263371d, 0.1054336766d, -0.1624711654d, 0.9867132919d, -0.9949487814d, -0.100383875d,
-0.6995302564d, 0.7146029809d, 0.5263414922d, -0.85027327d, -0.5395221479d, 0.841971408d, 0.6579370318d, 0.7530729462d,
0.01426758847d, -0.9998982128d, -0.6734383991d, 0.7392433447d, 0.639412098d, -0.7688642071d, 0.9211571421d, 0.3891908523d,
-0.146637214d, -0.9891903394d, -0.782318098d, 0.6228791163d, -0.5039610839d, -0.8637263605d, -0.7743120191d, -0.6328039957d,
};
private static final int PRECOMPUTE_RADIUS = 3;
private static final int PRECOMPUTE_SIZE = 1 + 2 * PRECOMPUTE_RADIUS;
private static final int NEARBY_CELLS_RADIUS = 2;
private static final int MAX_CONNECTION_RADIUS = 1;
private final long salt;
private final double frequency;
private final double cellularJitter;
private final LoadingCache<ChunkPos, CellChunk2D> cache;
public PseudoErosionSampler(long salt, double frequency, NoiseSampler lookup, double jitterModifier) {
this.salt = salt;
this.frequency = frequency;
this.cellularJitter = 0.43701595 * jitterModifier;
this.cache = Caffeine.newBuilder()
.maximumSize(64)
.build(v -> CellChunk2D.create(v, lookup, frequency, this.cellularJitter));
}
public double getNoiseRaw(long sl, double x, double y) {
int seed = (int) sl;
double finalDistance = Double.MAX_VALUE;
// Round sampled position to integers to derive grid coordinates
int gridX = fastRound(x);
int gridY = fastRound(y);
double[][][] cellData = new double[PRECOMPUTE_SIZE][PRECOMPUTE_SIZE][3];
for(int xi = -PRECOMPUTE_RADIUS; xi <= PRECOMPUTE_RADIUS; xi++) {
for(int yi = -PRECOMPUTE_RADIUS; yi <= PRECOMPUTE_RADIUS; yi++) {
cellData[xi+PRECOMPUTE_RADIUS][yi+PRECOMPUTE_RADIUS] = getData(seed, gridX+xi, gridY + yi);
}
}
// Iterate over nearby cells
for(int xi = -NEARBY_CELLS_RADIUS; xi <= NEARBY_CELLS_RADIUS; xi++) {
for(int yi = -NEARBY_CELLS_RADIUS; yi <= NEARBY_CELLS_RADIUS; yi++) {
// Find cell position with the lowest lookup value within moore neighborhood of neighbor
double lowestLookup = Double.MAX_VALUE;
double connectedCellX = 0;
double connectedCellY = 0;
for(int xni = xi - MAX_CONNECTION_RADIUS; xni <= xi + MAX_CONNECTION_RADIUS; xni++) {
for(int yni = yi - MAX_CONNECTION_RADIUS; yni <= yi + MAX_CONNECTION_RADIUS; yni++) {
double[] data = cellData[xni+PRECOMPUTE_RADIUS][yni+PRECOMPUTE_RADIUS];
double lookup = data[2];
if(lookup < lowestLookup) {
lowestLookup = lookup;
connectedCellX = data[0];
connectedCellY = data[1];
}
}
}
double[] data = cellData[xi+PRECOMPUTE_RADIUS][yi+PRECOMPUTE_RADIUS];
double cellX = data[0];
double cellY = data[1];
// Calculate SDF for line between the current cell position and the surrounding cell with the lowest lookup
double distance = lineSdf2D(x, y, cellX, cellY, connectedCellX, connectedCellY);
// Set final return to the lowest computed distance
finalDistance = fastMin(finalDistance, distance);
}
}
// Shows grid
// if(fastAbs(x-round(x)) > 0.5d - 0.01d || fastAbs(y-round(y)) > 0.5d - 0.01d) {
// return 0;
// }
return finalDistance;
}
private double[] getData(int seed, int x, int y) {
int chunkX = FastMath.floorDiv(x, CellChunk2D.SIZE);
int chunkY = FastMath.floorDiv(y, CellChunk2D.SIZE);
int xInChunk = x - chunkX * CellChunk2D.SIZE;
int yInChunk = y - chunkY * CellChunk2D.SIZE;
return cache.get(new ChunkPos(seed, chunkX, chunkY)).data[xInChunk][yInChunk];
}
/**
* Signed distance function of a line segment determined by two points
*/
private static double lineSdf2D(double x, double y, double x1, double y1, double x2, double y2) {
double x1dx = x - x1;
double y1dx = y - y1;
if(x1 == x2 && y1 == y2) // If positions are the same just return distance from point
return fastSqrt(pow2(x1dx) + pow2(y1dx));
double ldx = x1 - x2;
double ldy = y1 - y2;
double x2dx = x - x2;
double y2dx = y - y2;
double lt = (ldy * y1dx + ldx * x1dx) / (pow2(ldy) + pow2(ldx)); // Position along line
if(lt > 0) {
return fastSqrt(pow2(x1dx) + pow2(y1dx)); // Distance between point 1 and position
} else if(lt < -1) {
return fastSqrt(pow2(x2dx) + pow2(y2dx)); // Distance between point 2 and position
} else {
return fastAbs((ldy * x1dx - ldx * y1dx) / fastSqrt(pow2(ldx) + pow2(ldy))); // Distance from line
}
}
private static int jitterIdx2D(int seed, int x, int y) {
return hash(seed, x * PRIME_X, y * PRIME_Y) & (255 << 1);
}
public double getNoiseRaw(long sl, double x, double y, double z) {
// TODO
return 0;
}
@Override
public double noise(long seed, double x, double y) {
return getNoiseRaw(seed + salt, x * frequency, y * frequency);
}
@Override
public double noise(long seed, double x, double y, double z) {
return getNoiseRaw(seed + salt, x * frequency, y * frequency, z * frequency);
}
protected record CellChunk2D(double[][][] data) {
private static final int SIZE = 128;
public static CellChunk2D create(ChunkPos vec, NoiseSampler lookup, double frequency, double cellularJitter) {
double[][][] data = new double[SIZE][SIZE][3];
int chunkWorldX = vec.chunkX * SIZE;
int chunkWorldY = vec.chunkY * SIZE;
int x, y;
for(int lx = 0; lx < SIZE; lx++) {
x = chunkWorldX + lx;
for(int ly = 0; ly < SIZE; ly++) {
y = chunkWorldY + ly;
int jitterIdx = jitterIdx2D(vec.seed, x, y);
double jitterX = RAND_VECS_2D[jitterIdx] * cellularJitter;
double jitterY = RAND_VECS_2D[jitterIdx | 1] * cellularJitter;
double cellX = x + jitterX;
double cellY = y + jitterY;
// Transform to actual coordinates for lookup
double actualCellX = cellX / frequency;
double actualCellY = cellY / frequency;
double value = lookup.noise(vec.seed, actualCellX, actualCellY);
double[] d = data[lx][ly];
d[0] = cellX;
d[1] = cellY;
d[2] = value;
}
}
return new CellChunk2D(data);
}
public record ChunkPos(int seed, int chunkX, int chunkY) {
}
}
}
@@ -5,18 +5,19 @@
* 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 {
private final char character;
private final int index;
private final int line;
private final SourcePosition position;
public Char(char character, int index, int line) {
public Char(char character, SourcePosition position) {
this.character = character;
this.index = index;
this.line = line;
this.position = position;
}
public boolean is(char... tests) {
@@ -33,18 +34,23 @@ public class Char {
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() {
return character;
}
public int getIndex() {
return index;
}
public int getLine() {
return line;
}
public boolean isWhitespace() {
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.
*/
package com.dfsek.terra.addons.terrascript.tokenizer;
package com.dfsek.terra.addons.terrascript.lexer;
public class Token {
private final String content;
private final Type type;
private final Position start;
private final TokenType type;
private final SourcePosition start;
public Token(String content, Type type, Position start) {
public Token(String content, TokenType type, SourcePosition start) {
this.content = content;
this.type = type;
this.start = start;
@@ -23,7 +23,7 @@ public class Token {
return type + ": '" + content + "'";
}
public Type getType() {
public TokenType getType() {
return type;
}
@@ -31,63 +31,68 @@ public class Token {
return content;
}
public Position getPosition() {
public SourcePosition getPosition() {
return start;
}
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() {
return type.equals(Type.ADDITION_OPERATOR)
|| type.equals(Type.SUBTRACTION_OPERATOR)
|| type.equals(Type.MULTIPLICATION_OPERATOR)
|| type.equals(Type.DIVISION_OPERATOR)
|| type.equals(Type.EQUALS_OPERATOR)
|| type.equals(Type.NOT_EQUALS_OPERATOR)
|| type.equals(Type.LESS_THAN_OPERATOR)
|| type.equals(Type.GREATER_THAN_OPERATOR)
|| type.equals(Type.LESS_THAN_OR_EQUALS_OPERATOR)
|| type.equals(Type.GREATER_THAN_OR_EQUALS_OPERATOR)
|| type.equals(Type.BOOLEAN_OR)
|| type.equals(Type.BOOLEAN_AND)
|| type.equals(Type.MODULO_OPERATOR);
return type.equals(TokenType.PLUS)
|| type.equals(TokenType.MINUS)
|| type.equals(TokenType.STAR)
|| type.equals(TokenType.FORWARD_SLASH)
|| type.equals(TokenType.EQUALS_EQUALS)
|| type.equals(TokenType.BANG_EQUALS)
|| type.equals(TokenType.LESS)
|| type.equals(TokenType.GREATER)
|| type.equals(TokenType.LESS_EQUALS)
|| type.equals(TokenType.GREATER_EQUAL)
|| type.equals(TokenType.BOOLEAN_OR)
|| type.equals(TokenType.BOOLEAN_AND)
|| type.equals(TokenType.MODULO_OPERATOR);
}
public boolean isStrictNumericOperator() {
return type.equals(Type.SUBTRACTION_OPERATOR)
|| type.equals(Type.MULTIPLICATION_OPERATOR)
|| type.equals(Type.DIVISION_OPERATOR)
|| type.equals(Type.GREATER_THAN_OPERATOR)
|| type.equals(Type.LESS_THAN_OPERATOR)
|| type.equals(Type.LESS_THAN_OR_EQUALS_OPERATOR)
|| type.equals(Type.GREATER_THAN_OR_EQUALS_OPERATOR)
|| type.equals(Type.MODULO_OPERATOR);
return type.equals(TokenType.MINUS)
|| type.equals(TokenType.STAR)
|| type.equals(TokenType.FORWARD_SLASH)
|| type.equals(TokenType.GREATER)
|| type.equals(TokenType.LESS)
|| type.equals(TokenType.LESS_EQUALS)
|| type.equals(TokenType.GREATER_EQUAL)
|| type.equals(TokenType.MODULO_OPERATOR);
}
public boolean isStrictBooleanOperator() {
return type.equals(Type.BOOLEAN_AND)
|| type.equals(Type.BOOLEAN_OR);
return type.equals(TokenType.BOOLEAN_AND)
|| type.equals(TokenType.BOOLEAN_OR);
}
public boolean isVariableDeclaration() {
return type.equals(Type.STRING_VARIABLE)
|| type.equals(Type.BOOLEAN_VARIABLE)
|| type.equals(Type.NUMBER_VARIABLE);
return type.equals(TokenType.TYPE_STRING)
|| type.equals(TokenType.TYPE_BOOLEAN)
|| type.equals(TokenType.TYPE_NUMBER);
}
public boolean isLoopLike() {
return type.equals(Type.IF_STATEMENT)
|| type.equals(Type.WHILE_LOOP)
|| type.equals(Type.FOR_LOOP);
public boolean isControlStructure() {
return type.equals(TokenType.IF_STATEMENT)
|| type.equals(TokenType.WHILE_LOOP)
|| type.equals(TokenType.FOR_LOOP);
}
public boolean isIdentifier() {
return type.equals(Type.IDENTIFIER);
}
public enum Type {
public enum TokenType {
/**
* Function identifier or language keyword
*/
@@ -108,11 +113,11 @@ public class Token {
/**
* Beginning of group
*/
GROUP_BEGIN,
OPEN_PAREN,
/**
* Ending of group
*/
GROUP_END,
CLOSE_PAREN,
/**
* End of statement
*/
@@ -136,43 +141,43 @@ public class Token {
/**
* Boolean equals operator
*/
EQUALS_OPERATOR,
EQUALS_EQUALS,
/**
* Boolean not equals operator
*/
NOT_EQUALS_OPERATOR,
BANG_EQUALS,
/**
* Boolean greater than operator
*/
GREATER_THAN_OPERATOR,
GREATER,
/**
* Boolean less than operator
*/
LESS_THAN_OPERATOR,
LESS,
/**
* Boolean greater than or equal to operator
*/
GREATER_THAN_OR_EQUALS_OPERATOR,
GREATER_EQUAL,
/**
* Boolean less than or equal to operator
*/
LESS_THAN_OR_EQUALS_OPERATOR,
LESS_EQUALS,
/**
* Addition/concatenation operator
*/
ADDITION_OPERATOR,
PLUS,
/**
* Subtraction operator
*/
SUBTRACTION_OPERATOR,
MINUS,
/**
* Multiplication operator
*/
MULTIPLICATION_OPERATOR,
STAR,
/**
* Division operator
*/
DIVISION_OPERATOR,
FORWARD_SLASH,
/**
* Modulo operator.
*/
@@ -180,7 +185,7 @@ public class Token {
/**
* Boolean not operator
*/
BOOLEAN_NOT,
BANG,
/**
* Boolean or
*/
@@ -192,15 +197,19 @@ public class Token {
/**
* Numeric variable declaration
*/
NUMBER_VARIABLE,
TYPE_NUMBER,
/**
* String variable declaration
*/
STRING_VARIABLE,
TYPE_STRING,
/**
* Boolean variable declaration
*/
BOOLEAN_VARIABLE,
TYPE_BOOLEAN,
/**
* Void type declaration
*/
TYPE_VOID,
/**
* If statement declaration
*/
@@ -232,6 +241,10 @@ public class Token {
/**
* Else keyword
*/
ELSE
ELSE,
/**
* End of file
*/
END_OF_FILE
}
}
@@ -5,11 +5,11 @@
* 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 com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
public class EOFException extends TokenizerException {
@@ -17,11 +17,11 @@ public class EOFException extends TokenizerException {
@Serial
private static final long serialVersionUID = 3980047409902809440L;
public EOFException(String message, Position position) {
public EOFException(String message, SourcePosition position) {
super(message, position);
}
public EOFException(String message, Position position, Throwable cause) {
public EOFException(String message, SourcePosition position, Throwable cause) {
super(message, position, cause);
}
}
@@ -5,11 +5,11 @@
* 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 com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
public class FormatException extends TokenizerException {
@@ -17,11 +17,11 @@ public class FormatException extends TokenizerException {
@Serial
private static final long serialVersionUID = -791308012940744455L;
public FormatException(String message, Position position) {
public FormatException(String message, SourcePosition position) {
super(message, position);
}
public FormatException(String message, Position position, Throwable cause) {
public FormatException(String message, SourcePosition position, Throwable cause) {
super(message, position, cause);
}
}
@@ -5,12 +5,12 @@
* 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 com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.exceptions.ParseException;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public abstract class TokenizerException extends ParseException {
@@ -18,11 +18,11 @@ public abstract class TokenizerException extends ParseException {
@Serial
private static final long serialVersionUID = 2792384010083575420L;
public TokenizerException(String message, Position position) {
public TokenizerException(String message, SourcePosition position) {
super(message, position);
}
public TokenizerException(String message, Position position, Throwable cause) {
public TokenizerException(String message, SourcePosition position, Throwable cause) {
super(message, position, cause);
}
}
@@ -9,24 +9,26 @@ package com.dfsek.terra.addons.terrascript.parser;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import 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.lang.Block;
import com.dfsek.terra.addons.terrascript.parser.lang.Executable;
import com.dfsek.terra.addons.terrascript.parser.lang.Item;
import com.dfsek.terra.addons.terrascript.parser.lang.Keyword;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable.ReturnType;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression.ReturnType;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope.ScopeBuilder;
import com.dfsek.terra.addons.terrascript.parser.lang.constants.BooleanConstant;
import com.dfsek.terra.addons.terrascript.parser.lang.constants.ConstantExpression;
import com.dfsek.terra.addons.terrascript.parser.lang.constants.NumericConstant;
import com.dfsek.terra.addons.terrascript.parser.lang.constants.StringConstant;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.UserDefinedFunctionBuilder;
import com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow.BreakKeyword;
import com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow.ContinueKeyword;
import com.dfsek.terra.addons.terrascript.parser.lang.keywords.flow.FailKeyword;
@@ -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.IfKeyword;
import com.dfsek.terra.addons.terrascript.parser.lang.keywords.looplike.WhileKeyword;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BooleanAndOperation;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BooleanNotOperation;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BooleanOrOperation;
@@ -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.NumVariableReferenceNode;
import com.dfsek.terra.addons.terrascript.parser.lang.variables.reference.StrVariableReferenceNode;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.addons.terrascript.tokenizer.Token;
import com.dfsek.terra.addons.terrascript.tokenizer.Tokenizer;
import com.dfsek.terra.api.util.generic.pair.Pair;
@SuppressWarnings("unchecked")
public class Parser {
private final String data;
private final Map<String, FunctionBuilder<? extends Function<?>>> functions = new HashMap<>();
private final List<String> ignoredFunctions = new ArrayList<>();
public Parser(String data) {
this.data = data;
}
private final Lexer lexer;
public Parser registerFunction(String name, FunctionBuilder<? extends Function<?>> functionBuilder) {
functions.put(name, functionBuilder);
return this;
}
public Parser ignoreFunction(String name) {
ignoredFunctions.add(name);
return this;
public Parser(Lexer lexer) {
this.lexer = lexer;
}
/**
@@ -91,378 +78,447 @@ public class Parser {
*
* @throws ParseException If parsing fails.
*/
public Executable parse() {
ScopeBuilder scopeBuilder = new ScopeBuilder();
return new Executable(parseBlock(new Tokenizer(data), false, scopeBuilder), scopeBuilder);
public Executable parse(ScopeBuilder scopeBuilder) {
return new Executable(parseBlock(scopeBuilder, ReturnType.VOID), scopeBuilder);
}
private Keyword<?> parseLoopLike(Tokenizer tokens, boolean loop, ScopeBuilder scopeBuilder) throws ParseException {
Token identifier = tokens.consume();
ParserUtil.checkType(identifier, Token.Type.IF_STATEMENT, Token.Type.WHILE_LOOP, Token.Type.FOR_LOOP);
ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_BEGIN);
return switch(identifier.getType()) {
case FOR_LOOP -> parseForLoop(tokens, identifier.getPosition(), scopeBuilder);
case IF_STATEMENT -> parseIfStatement(tokens, identifier.getPosition(), loop, scopeBuilder);
case WHILE_LOOP -> parseWhileLoop(tokens, identifier.getPosition(), scopeBuilder);
default -> throw new UnsupportedOperationException(
"Unknown keyword " + identifier.getContent() + ": " + identifier.getPosition());
};
private WhileKeyword parseWhileLoop(ScopeBuilder scopeBuilder) {
SourcePosition start = lexer.consume("Expected 'while' keyword at beginning of while loop", TokenType.WHILE_LOOP).getPosition();
lexer.consume("Expected '(' proceeding 'while' keyword", TokenType.OPEN_PAREN);
scopeBuilder = scopeBuilder.innerLoopScope();
Expression<?> condition = parseExpression(scopeBuilder);
ParserUtil.ensureReturnType(condition, Expression.ReturnType.BOOLEAN);
lexer.consume("Expected ')' proceeding while loop condition", TokenType.CLOSE_PAREN);
return new WhileKeyword(parseStatementBlock(scopeBuilder, ReturnType.VOID), (Expression<Boolean>) condition,
start); // While loop
}
private WhileKeyword parseWhileLoop(Tokenizer tokens, Position start, ScopeBuilder scopeBuilder) {
Returnable<?> first = parseExpression(tokens, true, scopeBuilder);
ParserUtil.checkReturnType(first, Returnable.ReturnType.BOOLEAN);
private IfKeyword parseIfStatement(ScopeBuilder scopeBuilder) {
SourcePosition start = lexer.consume("Expected 'if' keyword at beginning of if statement", TokenType.IF_STATEMENT).getPosition();
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);
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);
lexer.consume("Expected ')' proceeding if statement condition", TokenType.CLOSE_PAREN);
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)) {
tokens.consume(); // Consume else.
if(tokens.get().getType().equals(Token.Type.IF_STATEMENT)) {
tokens.consume(); // Consume if.
Returnable<?> elseCondition = parseExpression(tokens, true, scopeBuilder);
ParserUtil.checkReturnType(elseCondition, Returnable.ReturnType.BOOLEAN);
elseIf.add(Pair.of((Returnable<Boolean>) elseCondition, parseStatementBlock(tokens, loop, scopeBuilder)));
while(lexer.hasNext() && lexer.current().isType(TokenType.ELSE)) {
lexer.consumeUnchecked(); // Consume else.
if(lexer.current().isType(TokenType.IF_STATEMENT)) {
lexer.consumeUnchecked(); // Consume if.
Expression<?> elseCondition = parseExpression(scopeBuilder);
ParserUtil.ensureReturnType(elseCondition, Expression.ReturnType.BOOLEAN);
elseIf.add(Pair.of((Expression<Boolean>) elseCondition, parseStatementBlock(scopeBuilder, ReturnType.VOID)));
} else {
elseBlock = parseStatementBlock(tokens, loop, scopeBuilder);
elseBlock = parseStatementBlock(scopeBuilder, ReturnType.VOID);
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) {
if(tokens.get().getType().equals(Token.Type.BLOCK_BEGIN)) {
ParserUtil.checkType(tokens.consume(), Token.Type.BLOCK_BEGIN);
Block block = parseBlock(tokens, loop, scopeBuilder);
ParserUtil.checkType(tokens.consume(), Token.Type.BLOCK_END);
private Block parseStatementBlock(ScopeBuilder scopeBuilder, ReturnType blockReturnType) {
if(lexer.current().isType(TokenType.BLOCK_BEGIN)) {
lexer.consumeUnchecked();
Block block = parseBlock(scopeBuilder, blockReturnType);
lexer.consume("Expected block end '}' after block statements", TokenType.BLOCK_END);
return block;
} else {
Position position = tokens.get().getPosition();
Block block = new Block(Collections.singletonList(parseItem(tokens, loop, scopeBuilder)), position);
ParserUtil.checkType(tokens.consume(), Token.Type.STATEMENT_END);
return block;
SourcePosition position = lexer.current().getPosition();
return new Block(Collections.singletonList(parseStatement(scopeBuilder)), position, blockReturnType);
}
}
private ForKeyword parseForLoop(Tokenizer tokens, Position start, ScopeBuilder scopeBuilder) {
scopeBuilder = scopeBuilder.sub(); // new scope
Token f = tokens.get();
ParserUtil.checkType(f, Token.Type.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, Token.Type.IDENTIFIER);
Item<?> initializer;
if(f.isVariableDeclaration()) {
VariableAssignmentNode<?> forVar = parseVariableDeclaration(tokens, scopeBuilder);
Token name = tokens.get();
if(functions.containsKey(name.getContent()) || scopeBuilder.contains(name.getContent()))
throw new ParseException(name.getContent() + " is already defined in this scope", name.getPosition());
initializer = forVar;
} else initializer = parseExpression(tokens, true, scopeBuilder);
ParserUtil.checkType(tokens.consume(), Token.Type.STATEMENT_END);
Returnable<?> conditional = parseExpression(tokens, true, scopeBuilder);
ParserUtil.checkReturnType(conditional, Returnable.ReturnType.BOOLEAN);
ParserUtil.checkType(tokens.consume(), Token.Type.STATEMENT_END);
private ForKeyword parseForLoop(ScopeBuilder scopeBuilder) {
SourcePosition start = lexer.consume("Expected 'for' keyword at beginning of for loop", TokenType.FOR_LOOP).getPosition();
lexer.consume("Expected '(' after 'for' keyword", TokenType.OPEN_PAREN);
scopeBuilder = scopeBuilder.innerLoopScope(); // new scope
Item<?> incrementer;
Token token = tokens.get();
if(scopeBuilder.contains(token.getContent())) { // Assume variable assignment
incrementer = parseAssignment(tokens, scopeBuilder);
} else incrementer = parseFunction(tokens, true, scopeBuilder);
Expression<?> initializer = switch(lexer.current().getType()) {
case TYPE_NUMBER, TYPE_STRING, TYPE_BOOLEAN -> {
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);
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);
}
private Returnable<?> parseExpression(Tokenizer tokens, boolean full, ScopeBuilder scopeBuilder) {
boolean booleanInverted = false; // Check for boolean not operator
boolean negate = false;
if(tokens.get().getType().equals(Token.Type.BOOLEAN_NOT)) {
booleanInverted = true;
tokens.consume();
} else if(tokens.get().getType().equals(Token.Type.SUBTRACTION_OPERATOR)) {
negate = true;
tokens.consume();
}
Token id = tokens.get();
ParserUtil.checkType(id, Token.Type.IDENTIFIER, Token.Type.BOOLEAN, Token.Type.STRING, Token.Type.NUMBER, Token.Type.GROUP_BEGIN);
Returnable<?> expression;
if(id.isConstant()) {
expression = parseConstantExpression(tokens);
} else if(id.getType().equals(Token.Type.GROUP_BEGIN)) { // Parse grouped expression
expression = parseGroup(tokens, scopeBuilder);
} else {
if(functions.containsKey(id.getContent()))
expression = parseFunction(tokens, false, scopeBuilder);
else if(scopeBuilder.contains(id.getContent())) {
ParserUtil.checkType(tokens.consume(), Token.Type.IDENTIFIER);
String varId = id.getContent();
ReturnType varType = scopeBuilder.getType(varId);
expression = switch(varType) {
case NUMBER -> new NumVariableReferenceNode(id.getPosition(), varType, scopeBuilder.getIndex(varId));
case STRING -> new StrVariableReferenceNode(id.getPosition(), varType, scopeBuilder.getIndex(varId));
case BOOLEAN -> new BoolVariableReferenceNode(id.getPosition(), varType, scopeBuilder.getIndex(varId));
default -> throw new ParseException("Illegal type for variable reference: " + varType, id.getPosition());
};
} else throw new ParseException("Unexpected token \" " + id.getContent() + "\"", id.getPosition());
}
if(booleanInverted) { // Invert operation if boolean not detected
ParserUtil.checkReturnType(expression, Returnable.ReturnType.BOOLEAN);
expression = new BooleanNotOperation((Returnable<Boolean>) expression, expression.getPosition());
} else if(negate) {
ParserUtil.checkReturnType(expression, Returnable.ReturnType.NUMBER);
expression = new NegationOperation((Returnable<Number>) expression, expression.getPosition());
}
if(full && tokens.get().isBinaryOperator()) { // Parse binary operations
return parseBinaryOperation(expression, tokens, scopeBuilder);
}
return expression;
private Expression<?> parseExpression(ScopeBuilder scopeBuilder) {
return parseLogicOr(scopeBuilder);
}
private ConstantExpression<?> parseConstantExpression(Tokenizer tokens) {
Token constantToken = tokens.consume();
Position position = constantToken.getPosition();
switch(constantToken.getType()) {
case NUMBER:
String content = constantToken.getContent();
return new NumericConstant(content.contains(".") ? Double.parseDouble(content) : Integer.parseInt(content), position);
case STRING:
return new StringConstant(constantToken.getContent(), position);
case BOOLEAN:
return new BooleanConstant(Boolean.parseBoolean(constantToken.getContent()), position);
default:
throw new UnsupportedOperationException(
"Unsupported constant token: " + constantToken.getType() + " at position: " + position);
}
private Expression<?> parseLogicOr(ScopeBuilder scopeBuilder) {
return parseLeftAssociativeBinaryOperation(this::parseLogicAnd, scopeBuilder, (op) -> {
ParserUtil.ensureReturnType(op.left, ReturnType.BOOLEAN);
ParserUtil.ensureReturnType(op.right, ReturnType.BOOLEAN);
}, Map.of(TokenType.BOOLEAN_OR, (op) -> new BooleanOrOperation((Expression<Boolean>) op.left, (Expression<Boolean>) op.right, op.operator.getPosition())));
}
private Returnable<?> parseGroup(Tokenizer tokens, ScopeBuilder scopeBuilder) {
ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_BEGIN);
Returnable<?> expression = parseExpression(tokens, true, scopeBuilder); // Parse inside of group as a separate expression
ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_END);
return expression;
private Expression<?> parseLogicAnd(ScopeBuilder scopeBuilder) {
return parseLeftAssociativeBinaryOperation(this::parseEquality, scopeBuilder, (op) -> {
ParserUtil.ensureReturnType(op.left, ReturnType.BOOLEAN);
ParserUtil.ensureReturnType(op.right, ReturnType.BOOLEAN);
}, 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,
ScopeBuilder scopeBuilder) {
Token binaryOperator = tokens.consume();
ParserUtil.checkBinaryOperator(binaryOperator);
Returnable<?> right = parseExpression(tokens, false, scopeBuilder);
Token other = tokens.get();
if(ParserUtil.hasPrecedence(binaryOperator.getType(), other.getType())) {
return assemble(left, parseBinaryOperation(right, tokens, scopeBuilder), binaryOperator);
} else if(other.isBinaryOperator()) {
return parseBinaryOperation(assemble(left, right, binaryOperator), tokens, scopeBuilder);
}
return assemble(left, right, binaryOperator);
private Expression<?> parseEquality(ScopeBuilder scopeBuilder) {
return parseLeftAssociativeBinaryOperation(this::parseComparison, scopeBuilder, Map.of(
TokenType.EQUALS_EQUALS, (op) -> new EqualsStatement((Expression<Object>) op.left, (Expression<Object>) op.right, op.operator.getPosition()),
TokenType.BANG_EQUALS, (op) -> new NotEqualsStatement((Expression<Object>) op.left, (Expression<Object>) op.right, op.operator.getPosition())
));
}
private BinaryOperation<?, ?> assemble(Returnable<?> left, Returnable<?> right, Token binaryOperator) {
if(binaryOperator.isStrictNumericOperator())
ParserUtil.checkArithmeticOperation(left, right, binaryOperator); // Numeric type checking
if(binaryOperator.isStrictBooleanOperator()) ParserUtil.checkBooleanOperation(left, right, binaryOperator); // Boolean type checking
switch(binaryOperator.getType()) {
case ADDITION_OPERATOR:
if(left.returnType().equals(Returnable.ReturnType.NUMBER) && right.returnType().equals(Returnable.ReturnType.NUMBER)) {
return new NumberAdditionOperation((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition());
private Expression<?> parseComparison(ScopeBuilder scopeBuilder) {
return parseLeftAssociativeBinaryOperation(this::parseTerm, scopeBuilder, (op) -> {
ParserUtil.ensureReturnType(op.left, ReturnType.NUMBER);
ParserUtil.ensureReturnType(op.right, ReturnType.NUMBER);
}, Map.of(
TokenType.LESS, (op) -> new LessThanStatement((Expression<Number>) op.left, (Expression<Number>) op.right, op.operator.getPosition()),
TokenType.LESS_EQUALS, (op) -> new LessThanOrEqualsStatement((Expression<Number>) op.left, (Expression<Number>) op.right, op.operator.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 SUBTRACTION_OPERATOR:
return new SubtractionOperation((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition());
case MULTIPLICATION_OPERATOR:
return new MultiplicationOperation((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition());
case DIVISION_OPERATOR:
return new DivisionOperation((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition());
case EQUALS_OPERATOR:
return new EqualsStatement((Returnable<Object>) left, (Returnable<Object>) right, binaryOperator.getPosition());
case NOT_EQUALS_OPERATOR:
return new NotEqualsStatement((Returnable<Object>) left, (Returnable<Object>) right, binaryOperator.getPosition());
case GREATER_THAN_OPERATOR:
return new GreaterThanStatement((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition());
case LESS_THAN_OPERATOR:
return new LessThanStatement((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition());
case GREATER_THAN_OR_EQUALS_OPERATOR:
return new GreaterOrEqualsThanStatement((Returnable<Number>) left, (Returnable<Number>) right,
binaryOperator.getPosition());
case LESS_THAN_OR_EQUALS_OPERATOR:
return new LessThanOrEqualsStatement((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition());
case BOOLEAN_AND:
return new BooleanAndOperation((Returnable<Boolean>) left, (Returnable<Boolean>) right, binaryOperator.getPosition());
case BOOLEAN_OR:
return new BooleanOrOperation((Returnable<Boolean>) left, (Returnable<Boolean>) right, binaryOperator.getPosition());
case MODULO_OPERATOR:
return new ModuloOperation((Returnable<Number>) left, (Returnable<Number>) right, binaryOperator.getPosition());
default:
throw new UnsupportedOperationException("Unsupported binary operator: " + binaryOperator.getType());
case MINUS -> {
ParserUtil.ensureReturnType(right, ReturnType.NUMBER);
yield new NegationOperation((Expression<Number>) right, operator.getPosition());
}
default -> throw new IllegalStateException();
};
}
return parsePrimary(scopeBuilder);
}
private VariableAssignmentNode<?> parseVariableDeclaration(Tokenizer tokens, ScopeBuilder scopeBuilder) {
Token type = tokens.consume();
ParserUtil.checkType(type, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, Token.Type.NUMBER_VARIABLE);
private Expression<?> parsePrimary(ScopeBuilder scopeBuilder) {
Token token = lexer.consumeUnchecked();
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
Token identifier = tokens.consume();
ParserUtil.checkType(identifier, Token.Type.IDENTIFIER);
if(functions.containsKey(identifier.getContent()) || scopeBuilder.contains(identifier.getContent()))
if (!type.isVariableDeclaration()) throw new ParseException("Expected type specification at beginning of variable declaration", type.getPosition());
if(scopeBuilder.containsVariable(identifier.getContent()))
throw new ParseException(identifier.getContent() + " is already defined in this scope", identifier.getPosition());
ParserUtil.checkType(tokens.consume(), Token.Type.ASSIGNMENT);
Returnable<?> value = parseExpression(tokens, true, scopeBuilder);
ParserUtil.checkReturnType(value, returnType);
String id = identifier.getContent();
Expression<?> value = parseExpression(scopeBuilder);
ParserUtil.ensureReturnType(value, ParserUtil.getVariableReturnType(type));
String variableName = identifier.getContent();
return switch(value.returnType()) {
case NUMBER -> new NumAssignmentNode((Returnable<Number>) value, identifier.getPosition(), scopeBuilder.num(id));
case STRING -> new StrAssignmentNode((Returnable<String>) value, identifier.getPosition(), scopeBuilder.str(id));
case BOOLEAN -> new BoolAssignmentNode((Returnable<Boolean>) value, identifier.getPosition(), scopeBuilder.bool(id));
case NUMBER -> new NumAssignmentNode((Expression<Number>) value, identifier.getPosition(),
scopeBuilder.declareNum(variableName));
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());
};
}
private Block parseBlock(Tokenizer tokens, boolean loop, ScopeBuilder scopeBuilder) {
List<Item<?>> parsedItems = new ArrayList<>();
private Expression<?> parseFunctionDeclaration(ScopeBuilder scopeBuilder, Token type, Token identifier) {
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()) {
Token token = tokens.get();
if(token.getType().equals(Token.Type.BLOCK_END)) break; // Stop parsing at block end.
Item<?> parsedItem = parseItem(tokens, loop, scopeBuilder);
if(parsedItem != Function.NULL) {
parsedItems.add(parsedItem);
}
if(tokens.hasNext() && !token.isLoopLike()) ParserUtil.checkType(tokens.consume(), Token.Type.STATEMENT_END);
ReturnType returnType = ParserUtil.getVariableReturnType(type);
ScopeBuilder functionBodyScope = scopeBuilder.functionScope();
// Declare parameter names into function body scope
List<Pair<Integer, ReturnType>> parameterInfo = getFunctionParameterDeclaration().stream().map(
arg -> Pair.of(switch(arg.getRight()) {
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) {
Token token = tokens.get();
if(loop) ParserUtil.checkType(token, Token.Type.IDENTIFIER, Token.Type.IF_STATEMENT, Token.Type.WHILE_LOOP, Token.Type.FOR_LOOP,
Token.Type.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE,
Token.Type.RETURN, Token.Type.BREAK, Token.Type.CONTINUE, Token.Type.FAIL);
else ParserUtil.checkType(token, Token.Type.IDENTIFIER, Token.Type.IF_STATEMENT, Token.Type.WHILE_LOOP, Token.Type.FOR_LOOP,
Token.Type.NUMBER_VARIABLE, Token.Type.STRING_VARIABLE, Token.Type.BOOLEAN_VARIABLE, Token.Type.RETURN,
Token.Type.FAIL);
private Block parseBlock(ScopeBuilder scopeBuilder, ReturnType blockReturnType) {
List<Expression<?>> expressions = new ArrayList<>();
scopeBuilder = scopeBuilder.innerScope(); // Create new inner scope for the block
SourcePosition startPosition = lexer.current().getPosition();
if(token.isLoopLike()) { // Parse loop-like tokens (if, while, etc)
return parseLoopLike(tokens, loop, scopeBuilder);
} else if(token.isIdentifier()) { // Parse identifiers
if(scopeBuilder.contains(token.getContent())) { // Assume variable assignment
return parseAssignment(tokens, scopeBuilder);
} else return parseFunction(tokens, true, scopeBuilder);
} else if(token.isVariableDeclaration()) {
return parseVariableDeclaration(tokens, scopeBuilder);
} else if(token.getType().equals(Token.Type.RETURN)) return new ReturnKeyword(tokens.consume().getPosition());
else if(token.getType().equals(Token.Type.BREAK)) return new BreakKeyword(tokens.consume().getPosition());
else if(token.getType().equals(Token.Type.CONTINUE)) return new ContinueKeyword(tokens.consume().getPosition());
else if(token.getType().equals(Token.Type.FAIL)) return new FailKeyword(tokens.consume().getPosition());
else throw new UnsupportedOperationException("Unexpected token " + token.getType() + ": " + token.getPosition());
boolean hasReturn = false;
// Parse each statement
while(lexer.hasNext() && !lexer.current().isType(TokenType.BLOCK_END)) {
Expression<?> expression = parseStatement(scopeBuilder);
if(expression != Expression.NOOP) {
expressions.add(expression);
}
if(expression instanceof ReturnKeyword returnKeyword) {
hasReturn = true;
if(returnKeyword.dataReturnType() != blockReturnType)
throw new ParseException(
"Invalid return type, expected " + blockReturnType + ", found " + returnKeyword.dataReturnType(),
expression.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) {
Token identifier = tokens.consume();
private Expression<?> parseStatement(ScopeBuilder scopeBuilder) {
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);
Returnable<?> value = parseExpression(tokens, true, scopeBuilder);
Expression<?> value = parseExpression(scopeBuilder);
String id = identifier.getContent();
ParserUtil.checkReturnType(value, scopeBuilder.getType(id));
ParserUtil.ensureReturnType(value, scopeBuilder.getVaraibleType(id));
ReturnType type = value.returnType();
return switch(type) {
case NUMBER -> new NumAssignmentNode((Returnable<Number>) value, identifier.getPosition(), scopeBuilder.getIndex(id));
case STRING -> new StrAssignmentNode((Returnable<String>) value, identifier.getPosition(), scopeBuilder.getIndex(id));
case BOOLEAN -> new BoolAssignmentNode((Returnable<Boolean>) value, identifier.getPosition(), scopeBuilder.getIndex(id));
case NUMBER -> new NumAssignmentNode((Expression<Number>) value, identifier.getPosition(), scopeBuilder.getIndex(id));
case STRING -> new StrAssignmentNode((Expression<String>) 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());
};
}
private Function<?> parseFunction(Tokenizer tokens, boolean fullStatement, ScopeBuilder scopeBuilder) {
Token identifier = tokens.consume();
ParserUtil.checkType(identifier, Token.Type.IDENTIFIER); // First token must be identifier
private Expression<?> parseFunctionInvocation(Token identifier, ScopeBuilder scopeBuilder) {
if(!scopeBuilder.containsFunction(identifier.getContent()))
throw new ParseException("Function '" + identifier.getContent() + "' is not defined in this scope", identifier.getPosition());
if(!functions.containsKey(identifier.getContent()))
throw new ParseException("No such function \"" + identifier.getContent() + "\"", identifier.getPosition());
FunctionBuilder<?> builder = scopeBuilder.getFunction(identifier.getContent());
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<Returnable<?>> args = getArgs(tokens, scopeBuilder); // Extract arguments, consume the rest.
ParserUtil.checkType(tokens.consume(), Token.Type.GROUP_END); // Remove body end
if(fullStatement) ParserUtil.checkType(tokens.get(), Token.Type.STATEMENT_END);
List<Expression<?>> args = new ArrayList<>();
while(!lexer.current().isType(TokenType.CLOSE_PAREN)) {
args.add(parseExpression(scopeBuilder));
if (lexer.current().isType(TokenType.CLOSE_PAREN)) break;
lexer.consume("Expected ',' between function arguments", TokenType.SEPARATOR);
}
lexer.consume("Expected ')' after function arguments", TokenType.CLOSE_PAREN);
if(ignoredFunctions.contains(identifier.getContent())) {
return Function.NULL;
return Expression.NOOP;
}
if(functions.containsKey(identifier.getContent())) {
FunctionBuilder<?> builder = functions.get(identifier.getContent());
if(builder.argNumber() != -1 && args.size() != builder.argNumber())
throw new ParseException("Expected " + builder.argNumber() + " arguments, found " + args.size(), identifier.getPosition());
for(int i = 0; i < args.size(); i++) {
Returnable<?> argument = args.get(i);
if(builder.getArgument(i) == null)
throw new ParseException("Unexpected argument at position " + i + " in function " + identifier.getContent(),
identifier.getPosition());
ParserUtil.checkReturnType(argument, builder.getArgument(i));
}
return builder.build(args, identifier.getPosition());
if(builder.argNumber() != -1 && args.size() != builder.argNumber())
throw new ParseException("Expected " + builder.argNumber() + " arguments, found " + args.size(), identifier.getPosition());
for(int i = 0; i < args.size(); i++) {
Expression<?> argument = args.get(i);
if(builder.getArgument(i) == null)
throw new ParseException("Unexpected argument at position " + i + " in function " + identifier.getContent(),
identifier.getPosition());
ParserUtil.ensureReturnType(argument, builder.getArgument(i));
}
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.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.lang.Returnable;
import com.dfsek.terra.addons.terrascript.tokenizer.Token;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public class ParserUtil {
private static final Map<Token.Type, Map<Token.Type, Boolean>> PRECEDENCE = new HashMap<>(); // If second has precedence, true.
private static final List<Token.Type> ARITHMETIC = Arrays.asList(Token.Type.ADDITION_OPERATOR, Token.Type.SUBTRACTION_OPERATOR,
Token.Type.MULTIPLICATION_OPERATOR, Token.Type.DIVISION_OPERATOR,
Token.Type.MODULO_OPERATOR);
private static final List<Token.Type> COMPARISON = Arrays.asList(Token.Type.EQUALS_OPERATOR, Token.Type.NOT_EQUALS_OPERATOR,
Token.Type.LESS_THAN_OPERATOR, Token.Type.LESS_THAN_OR_EQUALS_OPERATOR,
Token.Type.GREATER_THAN_OPERATOR,
Token.Type.GREATER_THAN_OR_EQUALS_OPERATOR);
// public static void ensureType(Token token, TokenType... expected) {
// for(TokenType type : expected) if(token.getType().equals(type)) return;
// throw new ParseException("Expected " + Arrays.toString(expected) + " but found " + token.getType(), token.getPosition());
// }
static { // Setup precedence
Map<Token.Type, Boolean> add = new HashMap<>(); // Addition/subtraction before Multiplication/division.
add.put(Token.Type.MULTIPLICATION_OPERATOR, true);
add.put(Token.Type.DIVISION_OPERATOR, true);
PRECEDENCE.put(Token.Type.ADDITION_OPERATOR, add);
PRECEDENCE.put(Token.Type.SUBTRACTION_OPERATOR, add);
Map<Token.Type, Boolean> numericBoolean = new HashMap<>();
ARITHMETIC.forEach(op -> numericBoolean.put(op, true)); // Numbers before comparison
COMPARISON.forEach(op -> PRECEDENCE.put(op, numericBoolean));
Map<Token.Type, Boolean> booleanOps = new HashMap<>();
ARITHMETIC.forEach(op -> booleanOps.put(op, true)); // Everything before boolean
COMPARISON.forEach(op -> booleanOps.put(op, true));
PRECEDENCE.put(Token.Type.BOOLEAN_AND, booleanOps);
PRECEDENCE.put(Token.Type.BOOLEAN_OR, booleanOps);
public static void ensureReturnType(Expression<?> returnable, Expression.ReturnType... types) {
for(Expression.ReturnType type : types) if(returnable.returnType().equals(type)) return;
throw new ParseException("Invalid type " + returnable.returnType() + ", expected " + (types.length == 1 ? types[0].toString() : "one of " + Arrays.toString(types)), returnable.getPosition());
}
public static void checkType(Token token, Token.Type... expected) {
for(Token.Type type : expected) if(token.getType().equals(type)) return;
throw new ParseException("Expected " + Arrays.toString(expected) + " but found " + token.getType(), token.getPosition());
}
public static void checkReturnType(Returnable<?> returnable, Returnable.ReturnType... types) {
for(Returnable.ReturnType type : types) if(returnable.returnType().equals(type)) return;
throw new ParseException("Expected " + Arrays.toString(types) + " but found " + returnable.returnType(), returnable.getPosition());
}
public static void checkArithmeticOperation(Returnable<?> left, Returnable<?> right, Token operation) {
if(!left.returnType().equals(Returnable.ReturnType.NUMBER) || !right.returnType().equals(Returnable.ReturnType.NUMBER)) {
throw new ParseException(
"Operation " + operation.getType() + " not supported between " + left.returnType() + " and " + right.returnType(),
operation.getPosition());
}
}
public static void checkBooleanOperation(Returnable<?> left, Returnable<?> right, Token operation) {
if(!left.returnType().equals(Returnable.ReturnType.BOOLEAN) || !right.returnType().equals(Returnable.ReturnType.BOOLEAN)) {
throw new ParseException(
"Operation " + operation.getType() + " not supported between " + left.returnType() + " and " + right.returnType(),
operation.getPosition());
}
}
public static void checkVarType(Token token, Returnable.ReturnType returnType) {
if(returnType.equals(Returnable.ReturnType.STRING) && token.getType().equals(Token.Type.STRING_VARIABLE)) return;
if(returnType.equals(Returnable.ReturnType.NUMBER) && token.getType().equals(Token.Type.NUMBER_VARIABLE)) return;
if(returnType.equals(Returnable.ReturnType.BOOLEAN) && token.getType().equals(Token.Type.BOOLEAN_VARIABLE)) return;
throw new ParseException("Type mismatch, cannot convert from " + returnType + " to " + token.getType(), token.getPosition());
}
/**
* Checks if token is a binary operator
*
* @param token Token to check
*
* @throws ParseException If token isn't a binary operator
*/
public static void checkBinaryOperator(Token token) {
if(!token.isBinaryOperator())
throw new ParseException("Expected binary operator, found " + token.getType(), token.getPosition());
}
public static Returnable.ReturnType getVariableReturnType(Token varToken) {
public static Expression.ReturnType getVariableReturnType(Token varToken) {
return switch(varToken.getType()) {
case NUMBER_VARIABLE -> Returnable.ReturnType.NUMBER;
case STRING_VARIABLE -> Returnable.ReturnType.STRING;
case BOOLEAN_VARIABLE -> Returnable.ReturnType.BOOLEAN;
case TYPE_NUMBER -> Expression.ReturnType.NUMBER;
case TYPE_STRING -> Expression.ReturnType.STRING;
case TYPE_BOOLEAN -> Expression.ReturnType.BOOLEAN;
case TYPE_VOID -> Expression.ReturnType.VOID;
default -> throw new ParseException("Unexpected token " + varToken.getType() + "; expected variable declaration",
varToken.getPosition());
};
}
public static boolean hasPrecedence(Token.Type first, Token.Type second) {
if(!PRECEDENCE.containsKey(first)) return false;
Map<Token.Type, Boolean> pre = PRECEDENCE.get(first);
if(!pre.containsKey(second)) return false;
return pre.get(second);
}
}
@@ -9,30 +9,30 @@ package com.dfsek.terra.addons.terrascript.parser.exceptions;
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 {
@Serial
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);
this.position = position;
}
public ParseException(String message, Position position, Throwable cause) {
public ParseException(String message, SourcePosition position, Throwable cause) {
super(message, cause);
this.position = position;
}
@Override
public String getMessage() {
return super.getMessage() + ": " + position;
return "Error at " + position + ": " + super.getMessage();
}
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
}
@@ -9,35 +9,43 @@ package com.dfsek.terra.addons.terrascript.parser.lang;
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<?>> {
private final List<Item<?>> items;
private final Position position;
public class Block implements Expression<EvaluationInfo<?>> {
private final List<Expression<?>> items;
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.position = position;
this.returnType = returnType;
}
@Override
public ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) {
for(Item<?> item : items) {
Object result = item.apply(implementationArguments, scope);
if(result instanceof ReturnInfo<?> level) {
if(!level.getLevel().equals(ReturnLevel.NONE)) return level;
public ReturnType returnType() {
return returnType;
}
@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
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
public enum ReturnLevel {
public enum EvaluationLevel {
NONE(false),
BREAK(false),
CONTINUE(false),
@@ -46,7 +54,7 @@ public class Block implements Item<Block.ReturnInfo<?>> {
private final boolean returnFast;
ReturnLevel(boolean returnFast) {
EvaluationLevel(boolean returnFast) {
this.returnFast = returnFast;
}
@@ -56,21 +64,6 @@ public class Block implements Item<Block.ReturnInfo<?>> {
}
public static class ReturnInfo<T> {
private final ReturnLevel level;
private final T data;
public ReturnInfo(ReturnLevel level, T data) {
this.level = level;
this.data = data;
}
public ReturnLevel getLevel() {
return level;
}
public T getData() {
return data;
}
public record EvaluationInfo<T extends Expression<?>>(EvaluationLevel level, T data) {
}
}
@@ -7,13 +7,13 @@ import com.dfsek.terra.addons.terrascript.parser.lang.Scope.ScopeBuilder;
public class Executable {
private final Block script;
private final ThreadLocal<Scope> scope;
public Executable(Block script, ScopeBuilder scopeBuilder) {
this.script = script;
this.scope = ThreadLocal.withInitial(scopeBuilder::build);
}
public boolean execute(ImplementationArguments arguments) {
return script.apply(arguments, scope.get()).getLevel() != Block.ReturnLevel.FAIL;
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;
/**
* Arguments passed to {@link Item}s by the implementation
* Arguments passed to {@link Expression}s by the implementation
*/
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;
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.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;
@@ -46,28 +48,57 @@ public class Scope {
}
public static final class ScopeBuilder {
private final Map<String, FunctionBuilder<? extends Function<?>>> functions;
private final Map<String, Pair<Integer, ReturnType>> indices;
private int numSize, boolSize, strSize = 0;
private ScopeBuilder parent;
private boolean inLoop;
public ScopeBuilder() {
this.functions = new HashMap<>();
this.indices = new HashMap<>();
}
private ScopeBuilder(ScopeBuilder parent) {
private ScopeBuilder(ScopeBuilder parent, boolean inLoop) {
this.parent = parent;
this.numSize = parent.numSize;
this.boolSize = parent.boolSize;
this.strSize = parent.strSize;
this.functions = new HashMap<>(parent.functions);
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() {
return new Scope(numSize, boolSize, strSize);
}
public ScopeBuilder sub() {
return new ScopeBuilder(this);
public ScopeBuilder innerScope() {
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) {
@@ -76,8 +107,12 @@ public class Scope {
}
return id;
}
public int num(String id) {
public boolean isInLoop() {
return inLoop;
}
public int declareNum(String id) {
int num = numSize;
indices.put(check(id), Pair.of(num, ReturnType.NUMBER));
numSize++;
@@ -85,7 +120,7 @@ public class Scope {
return num;
}
public int str(String id) {
public int declareStr(String id) {
int str = strSize;
indices.put(check(id), Pair.of(str, ReturnType.STRING));
strSize++;
@@ -93,7 +128,7 @@ public class Scope {
return str;
}
public int bool(String id) {
public int declareBool(String id) {
int bool = boolSize;
indices.put(check(id), Pair.of(bool, ReturnType.BOOLEAN));
boolSize++;
@@ -107,14 +142,14 @@ public class Scope {
parent.updateBoolSize(size);
}
}
private void updateNumSize(int size) {
this.numSize = FastMath.max(numSize, size);
if(parent != null) {
parent.updateNumSize(size);
}
}
private void updateStrSize(int size) {
this.strSize = FastMath.max(strSize, size);
if(parent != null) {
@@ -126,12 +161,11 @@ public class Scope {
return indices.get(id).getLeft();
}
public ReturnType getType(String id) {
public ReturnType getVaraibleType(String id) {
return indices.get(id).getRight();
}
public boolean contains(String id) {
public boolean containsVariable(String 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;
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BooleanConstant extends ConstantExpression<Boolean> {
private final boolean constant;
public BooleanConstant(Boolean constant, Position position) {
public BooleanConstant(Boolean constant, SourcePosition position) {
super(constant, position);
this.constant = constant;
}
@@ -7,28 +7,28 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public abstract class ConstantExpression<T> implements Returnable<T> {
public abstract class ConstantExpression<T> implements Expression<T> {
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.position = position;
}
@Override
public T apply(ImplementationArguments implementationArguments, Scope scope) {
public T evaluate(ImplementationArguments implementationArguments, Scope scope) {
return constant;
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
@@ -7,16 +7,16 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class NumericConstant extends ConstantExpression<Number> {
private final double constant;
public NumericConstant(Number constant, Position position) {
public NumericConstant(Number constant, SourcePosition position) {
super(constant, position);
this.constant = constant.doubleValue();
}
@@ -27,7 +27,7 @@ public class NumericConstant extends ConstantExpression<Number> {
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.NUMBER;
public Expression.ReturnType returnType() {
return Expression.ReturnType.NUMBER;
}
}
@@ -7,17 +7,17 @@
package com.dfsek.terra.addons.terrascript.parser.lang.constants;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public class StringConstant extends ConstantExpression<String> {
public StringConstant(String constant, Position position) {
public StringConstant(String constant, SourcePosition position) {
super(constant, position);
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.STRING;
public Expression.ReturnType returnType() {
return Expression.ReturnType.STRING;
}
}
@@ -7,37 +7,20 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public interface Function<T> extends Returnable<T> {
Function<?> NULL = new Function<>() {
@Override
public ReturnType returnType() {
return null;
}
@Override
public Object apply(ImplementationArguments implementationArguments, Scope scope) {
return null;
}
@Override
public Position getPosition() {
return null;
}
};
public interface Function<T> extends Expression<T> {
@Override
default double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return ((Number) apply(implementationArguments, scope)).doubleValue();
return ((Number) evaluate(implementationArguments, scope)).doubleValue();
}
@Override
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 com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
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();
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;
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.Keyword;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BreakKeyword implements Keyword<Block.ReturnInfo<?>> {
private final Position position;
public class BreakKeyword implements Keyword<EvaluationInfo<?>> {
private final SourcePosition position;
public BreakKeyword(Position position) {
public BreakKeyword(SourcePosition position) {
this.position = position;
}
@Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) {
return new Block.ReturnInfo<>(Block.ReturnLevel.BREAK, null);
public EvaluationInfo<?> evaluate(ImplementationArguments implementationArguments, Scope scope) {
return new EvaluationInfo<>(EvaluationLevel.BREAK, null);
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
@@ -7,27 +7,28 @@
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.Keyword;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ContinueKeyword implements Keyword<Block.ReturnInfo<?>> {
private final Position position;
public class ContinueKeyword implements Keyword<EvaluationInfo<?>> {
private final SourcePosition position;
public ContinueKeyword(Position position) {
public ContinueKeyword(SourcePosition position) {
this.position = position;
}
@Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) {
return new Block.ReturnInfo<>(Block.ReturnLevel.CONTINUE, null);
public EvaluationInfo<?> evaluate(ImplementationArguments implementationArguments, Scope scope) {
return new EvaluationInfo<>(EvaluationLevel.CONTINUE, null);
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
@@ -7,27 +7,28 @@
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.Keyword;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class FailKeyword implements Keyword<Block.ReturnInfo<?>> {
private final Position position;
public class FailKeyword implements Keyword<EvaluationInfo<?>> {
private final SourcePosition position;
public FailKeyword(Position position) {
public FailKeyword(SourcePosition position) {
this.position = position;
}
@Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) {
return new Block.ReturnInfo<>(Block.ReturnLevel.FAIL, null);
public EvaluationInfo<?> evaluate(ImplementationArguments implementationArguments, Scope scope) {
return new EvaluationInfo<>(EvaluationLevel.FAIL, null);
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
@@ -7,27 +7,34 @@
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.Keyword;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ReturnKeyword implements Keyword<Block.ReturnInfo<?>> {
private final Position position;
public class ReturnKeyword implements Keyword<EvaluationInfo<?>> {
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;
}
@Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) {
return new Block.ReturnInfo<>(Block.ReturnLevel.RETURN, null);
public EvaluationInfo<?> evaluate(ImplementationArguments implementationArguments, Scope scope) {
return new EvaluationInfo<>(EvaluationLevel.RETURN, data);
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
@@ -35,4 +42,12 @@ public class ReturnKeyword implements Keyword<Block.ReturnInfo<?>> {
public ReturnType returnType() {
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;
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.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.Item;
import com.dfsek.terra.addons.terrascript.parser.lang.Keyword;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ForKeyword implements Keyword<Block.ReturnInfo<?>> {
public class ForKeyword implements Keyword<Block.EvaluationInfo<?>> {
private final Block conditional;
private final Item<?> initializer;
private final Returnable<Boolean> statement;
private final Item<?> incrementer;
private final Position position;
private final Expression<?> initializer;
private final Expression<Boolean> statement;
private final Expression<?> incrementer;
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.initializer = initializer;
this.statement = statement;
@@ -32,19 +33,19 @@ public class ForKeyword implements Keyword<Block.ReturnInfo<?>> {
}
@Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) {
for(initializer.apply(implementationArguments, scope);
statement.apply(implementationArguments, scope);
incrementer.apply(implementationArguments, scope)) {
Block.ReturnInfo<?> level = conditional.apply(implementationArguments, scope);
if(level.getLevel().equals(Block.ReturnLevel.BREAK)) break;
if(level.getLevel().isReturnFast()) return level;
public Block.EvaluationInfo<?> evaluate(ImplementationArguments implementationArguments, Scope scope) {
for(initializer.evaluate(implementationArguments, scope);
statement.evaluate(implementationArguments, scope);
incrementer.evaluate(implementationArguments, scope)) {
Block.EvaluationInfo<?> level = conditional.evaluate(implementationArguments, scope);
if(level.level().equals(EvaluationLevel.BREAK)) break;
if(level.level().isReturnFast()) return level;
}
return new Block.ReturnInfo<>(Block.ReturnLevel.NONE, null);
return new Block.EvaluationInfo<>(EvaluationLevel.NONE, null);
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
@@ -11,24 +11,25 @@ import org.jetbrains.annotations.Nullable;
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.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.Keyword;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.util.generic.pair.Pair;
public class IfKeyword implements Keyword<Block.ReturnInfo<?>> {
public class IfKeyword implements Keyword<Block.EvaluationInfo<?>> {
private final Block conditional;
private final Returnable<Boolean> statement;
private final Position position;
private final List<Pair<Returnable<Boolean>, Block>> elseIf;
private final Expression<Boolean> statement;
private final SourcePosition position;
private final List<Pair<Expression<Boolean>, Block>> elseIf;
private final Block elseBlock;
public IfKeyword(Block conditional, Returnable<Boolean> statement, List<Pair<Returnable<Boolean>, Block>> elseIf,
@Nullable Block elseBlock, Position position) {
public IfKeyword(Block conditional, Expression<Boolean> statement, List<Pair<Expression<Boolean>, Block>> elseIf,
@Nullable Block elseBlock, SourcePosition position) {
this.conditional = conditional;
this.statement = statement;
this.position = position;
@@ -37,21 +38,21 @@ public class IfKeyword implements Keyword<Block.ReturnInfo<?>> {
}
@Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) {
if(statement.apply(implementationArguments, scope)) return conditional.apply(implementationArguments, scope);
public Block.EvaluationInfo<?> evaluate(ImplementationArguments implementationArguments, Scope scope) {
if(statement.evaluate(implementationArguments, scope)) return conditional.evaluate(implementationArguments, scope);
else {
for(Pair<Returnable<Boolean>, Block> pair : elseIf) {
if(pair.getLeft().apply(implementationArguments, scope)) {
return pair.getRight().apply(implementationArguments, scope);
for(Pair<Expression<Boolean>, Block> pair : elseIf) {
if(pair.getLeft().evaluate(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
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
@@ -7,37 +7,39 @@
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.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.Keyword;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class WhileKeyword implements Keyword<Block.ReturnInfo<?>> {
public class WhileKeyword implements Keyword<EvaluationInfo<?>> {
private final Block conditional;
private final Returnable<Boolean> statement;
private final Position position;
private final Expression<Boolean> statement;
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.statement = statement;
this.position = position;
}
@Override
public Block.ReturnInfo<?> apply(ImplementationArguments implementationArguments, Scope scope) {
while(statement.apply(implementationArguments, scope)) {
Block.ReturnInfo<?> level = conditional.apply(implementationArguments, scope);
if(level.getLevel().equals(Block.ReturnLevel.BREAK)) break;
if(level.getLevel().isReturnFast()) return level;
public EvaluationInfo<?> evaluate(ImplementationArguments implementationArguments, Scope scope) {
while(statement.evaluate(implementationArguments, scope)) {
EvaluationInfo<?> level = conditional.evaluate(implementationArguments, scope);
if(level.level().equals(EvaluationLevel.BREAK)) break;
if(level.level().isReturnFast()) return level;
}
return new Block.ReturnInfo<>(Block.ReturnLevel.NONE, null);
return new EvaluationInfo<>(EvaluationLevel.NONE, null);
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
@@ -7,23 +7,23 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public abstract class BinaryOperation<I, O> implements Returnable<O> {
protected final Returnable<I> left;
protected final Returnable<I> right;
private final Position start;
public abstract class BinaryOperation<I, O> implements Expression<O> {
protected final Expression<I> left;
protected final Expression<I> right;
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.right = right;
this.start = start;
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return start;
}
}
@@ -7,14 +7,14 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BooleanAndOperation extends BinaryOperation<Boolean, Boolean> {
public BooleanAndOperation(Returnable<Boolean> left, Returnable<Boolean> right, Position start) {
public BooleanAndOperation(Expression<Boolean> left, Expression<Boolean> right, SourcePosition start) {
super(left, right, start);
}
@@ -24,7 +24,7 @@ public class BooleanAndOperation extends BinaryOperation<Boolean, Boolean> {
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@@ -7,19 +7,19 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BooleanNotOperation extends UnaryOperation<Boolean> {
public BooleanNotOperation(Returnable<Boolean> input, Position position) {
public BooleanNotOperation(Expression<Boolean> input, SourcePosition position) {
super(input, position);
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@@ -7,19 +7,19 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BooleanOrOperation extends BinaryOperation<Boolean, Boolean> {
public BooleanOrOperation(Returnable<Boolean> left, Returnable<Boolean> right, Position start) {
public BooleanOrOperation(Expression<Boolean> left, Expression<Boolean> right, SourcePosition start) {
super(left, right, start);
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@@ -27,7 +27,7 @@ public class BooleanOrOperation extends BinaryOperation<Boolean, Boolean> {
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
return left.applyBoolean(implementationArguments, scope) || right.applyBoolean(implementationArguments, scope);
}
@Override
public ReturnType returnType() {
return ReturnType.BOOLEAN;
@@ -7,14 +7,14 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ConcatenationOperation extends BinaryOperation<Object, Object> {
public ConcatenationOperation(Returnable<Object> left, Returnable<Object> right, Position position) {
public ConcatenationOperation(Expression<Object> left, Expression<Object> right, SourcePosition position) {
super(left, right, position);
}
@@ -30,12 +30,12 @@ public class ConcatenationOperation extends BinaryOperation<Object, Object> {
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.STRING;
public Expression.ReturnType returnType() {
return Expression.ReturnType.STRING;
}
@Override
public Object apply(ImplementationArguments implementationArguments, Scope scope) {
return toString(left.apply(implementationArguments, scope)) + toString(right.apply(implementationArguments, scope));
public Object evaluate(ImplementationArguments implementationArguments, Scope 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;
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class DivisionOperation extends BinaryOperation<Number, Number> {
public DivisionOperation(Returnable<Number> left, Returnable<Number> right, Position position) {
public DivisionOperation(Expression<Number> left, Expression<Number> right, SourcePosition position) {
super(left, right, position);
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.NUMBER;
public Expression.ReturnType returnType() {
return Expression.ReturnType.NUMBER;
}
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope);
}
@@ -7,19 +7,19 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ModuloOperation extends BinaryOperation<Number, Number> {
public ModuloOperation(Returnable<Number> left, Returnable<Number> right, Position start) {
public ModuloOperation(Expression<Number> left, Expression<Number> right, SourcePosition start) {
super(left, right, start);
}
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope);
}
@@ -27,7 +27,7 @@ public class ModuloOperation extends BinaryOperation<Number, Number> {
public double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) % right.applyDouble(implementationArguments, scope);
}
@Override
public ReturnType returnType() {
return ReturnType.NUMBER;
@@ -7,19 +7,19 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class MultiplicationOperation extends BinaryOperation<Number, Number> {
public MultiplicationOperation(Returnable<Number> left, Returnable<Number> right, Position position) {
public MultiplicationOperation(Expression<Number> left, Expression<Number> right, SourcePosition position) {
super(left, right, position);
}
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope);
}
@@ -27,7 +27,7 @@ public class MultiplicationOperation extends BinaryOperation<Number, Number> {
public double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) * right.applyDouble(implementationArguments, scope);
}
@Override
public ReturnType returnType() {
return ReturnType.NUMBER;
@@ -7,14 +7,14 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class NegationOperation extends UnaryOperation<Number> {
public NegationOperation(Returnable<Number> input, Position position) {
public NegationOperation(Expression<Number> input, SourcePosition position) {
super(input, position);
}
@@ -24,7 +24,7 @@ public class NegationOperation extends UnaryOperation<Number> {
}
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope);
}
@@ -7,19 +7,19 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class NumberAdditionOperation extends BinaryOperation<Number, Number> {
public NumberAdditionOperation(Returnable<Number> left, Returnable<Number> right, Position position) {
public NumberAdditionOperation(Expression<Number> left, Expression<Number> right, SourcePosition position) {
super(left, right, position);
}
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope);
}
@@ -27,7 +27,7 @@ public class NumberAdditionOperation extends BinaryOperation<Number, Number> {
public double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) + right.applyDouble(implementationArguments, scope);
}
@Override
public ReturnType returnType() {
return ReturnType.NUMBER;
@@ -7,19 +7,19 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class SubtractionOperation extends BinaryOperation<Number, Number> {
public SubtractionOperation(Returnable<Number> left, Returnable<Number> right, Position position) {
public SubtractionOperation(Expression<Number> left, Expression<Number> right, SourcePosition position) {
super(left, right, position);
}
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyDouble(implementationArguments, scope);
}
@@ -27,7 +27,7 @@ public class SubtractionOperation extends BinaryOperation<Number, Number> {
public double applyDouble(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) - right.applyDouble(implementationArguments, scope);
}
@Override
public ReturnType returnType() {
return ReturnType.NUMBER;
@@ -7,21 +7,21 @@
package com.dfsek.terra.addons.terrascript.parser.lang.operations;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public abstract class UnaryOperation<T> implements Returnable<T> {
protected final Returnable<T> input;
private final Position position;
public abstract class UnaryOperation<T> implements Expression<T> {
protected final Expression<T> input;
private final SourcePosition position;
public UnaryOperation(Returnable<T> input, Position position) {
public UnaryOperation(Expression<T> input, SourcePosition position) {
this.input = input;
this.position = position;
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
}
@@ -9,40 +9,40 @@ package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements;
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import static com.dfsek.terra.api.util.MathUtil.EPSILON;
public class EqualsStatement extends BinaryOperation<Object, Boolean> {
public EqualsStatement(Returnable<Object> left, Returnable<Object> right, Position position) {
public EqualsStatement(Expression<Object> left, Expression<Object> right, SourcePosition position) {
super(left, right, position);
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN;
public Expression.ReturnType returnType() {
return Expression.ReturnType.BOOLEAN;
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@Override
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
Object leftValue = left.apply(implementationArguments, scope);
Object rightValue = right.apply(implementationArguments, scope);
Object leftValue = left.evaluate(implementationArguments, scope);
Object rightValue = right.evaluate(implementationArguments, scope);
if(leftValue instanceof Number l && rightValue instanceof Number r) {
return FastMath.abs(l.doubleValue() - r.doubleValue()) <= EPSILON;
}
return leftValue.equals(rightValue);
}
}
@@ -7,25 +7,25 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class GreaterOrEqualsThanStatement extends BinaryOperation<Number, Boolean> {
public GreaterOrEqualsThanStatement(Returnable<Number> left, Returnable<Number> right, Position position) {
public GreaterOrEqualsThanStatement(Expression<Number> left, Expression<Number> right, SourcePosition position) {
super(left, right, position);
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN;
public Expression.ReturnType returnType() {
return Expression.ReturnType.BOOLEAN;
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@@ -7,21 +7,21 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class GreaterThanStatement extends BinaryOperation<Number, Boolean> {
public GreaterThanStatement(Returnable<Number> left, Returnable<Number> right, Position position) {
public GreaterThanStatement(Expression<Number> left, Expression<Number> right, SourcePosition position) {
super(left, right, position);
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@@ -31,7 +31,7 @@ public class GreaterThanStatement extends BinaryOperation<Number, Boolean> {
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN;
public Expression.ReturnType returnType() {
return Expression.ReturnType.BOOLEAN;
}
}
@@ -7,21 +7,21 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class LessThanOrEqualsStatement extends BinaryOperation<Number, Boolean> {
public LessThanOrEqualsStatement(Returnable<Number> left, Returnable<Number> right, Position position) {
public LessThanOrEqualsStatement(Expression<Number> left, Expression<Number> right, SourcePosition position) {
super(left, right, position);
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@@ -29,9 +29,9 @@ public class LessThanOrEqualsStatement extends BinaryOperation<Number, Boolean>
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) <= right.applyDouble(implementationArguments, scope);
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN;
public Expression.ReturnType returnType() {
return Expression.ReturnType.BOOLEAN;
}
}
@@ -7,21 +7,21 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class LessThanStatement extends BinaryOperation<Number, Boolean> {
public LessThanStatement(Returnable<Number> left, Returnable<Number> right, Position position) {
public LessThanStatement(Expression<Number> left, Expression<Number> right, SourcePosition position) {
super(left, right, position);
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@@ -29,9 +29,9 @@ public class LessThanStatement extends BinaryOperation<Number, Boolean> {
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
return left.applyDouble(implementationArguments, scope) < right.applyDouble(implementationArguments, scope);
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN;
public Expression.ReturnType returnType() {
return Expression.ReturnType.BOOLEAN;
}
}
@@ -9,29 +9,29 @@ package com.dfsek.terra.addons.terrascript.parser.lang.operations.statements;
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.operations.BinaryOperation;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import static com.dfsek.terra.api.util.MathUtil.EPSILON;
public class NotEqualsStatement extends BinaryOperation<Object, Boolean> {
public NotEqualsStatement(Returnable<Object> left, Returnable<Object> right, Position position) {
public NotEqualsStatement(Expression<Object> left, Expression<Object> right, SourcePosition position) {
super(left, right, position);
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return applyBoolean(implementationArguments, scope);
}
@Override
public boolean applyBoolean(ImplementationArguments implementationArguments, Scope scope) {
Object leftValue = left.apply(implementationArguments, scope);
Object rightValue = right.apply(implementationArguments, scope);
Object leftValue = left.evaluate(implementationArguments, scope);
Object rightValue = right.evaluate(implementationArguments, scope);
if(leftValue instanceof Number l && rightValue instanceof Number r) {
return FastMath.abs(l.doubleValue() - r.doubleValue()) > EPSILON;
}
@@ -40,7 +40,7 @@ public class NotEqualsStatement extends BinaryOperation<Object, Boolean> {
}
@Override
public Returnable.ReturnType returnType() {
return Returnable.ReturnType.BOOLEAN;
public Expression.ReturnType returnType() {
return Expression.ReturnType.BOOLEAN;
}
}
@@ -7,15 +7,15 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public class BooleanVariable implements Variable<Boolean> {
private final Position position;
private final SourcePosition position;
private Boolean value;
public BooleanVariable(Boolean value, Position position) {
public BooleanVariable(Boolean value, SourcePosition position) {
this.value = value;
this.position = position;
}
@@ -31,12 +31,12 @@ public class BooleanVariable implements Variable<Boolean> {
}
@Override
public Returnable.ReturnType getType() {
return Returnable.ReturnType.BOOLEAN;
public Expression.ReturnType getType() {
return Expression.ReturnType.BOOLEAN;
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
}
@@ -7,15 +7,15 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public class NumberVariable implements Variable<Number> {
private final Position position;
private final SourcePosition position;
private Number value;
public NumberVariable(Number value, Position position) {
public NumberVariable(Number value, SourcePosition position) {
this.value = value;
this.position = position;
}
@@ -31,12 +31,12 @@ public class NumberVariable implements Variable<Number> {
}
@Override
public Returnable.ReturnType getType() {
return Returnable.ReturnType.NUMBER;
public Expression.ReturnType getType() {
return Expression.ReturnType.NUMBER;
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
}
@@ -7,15 +7,15 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public class StringVariable implements Variable<String> {
private final Position position;
private final SourcePosition position;
private String value;
public StringVariable(String value, Position position) {
public StringVariable(String value, SourcePosition position) {
this.value = value;
this.position = position;
}
@@ -31,12 +31,12 @@ public class StringVariable implements Variable<String> {
}
@Override
public Returnable.ReturnType getType() {
return Returnable.ReturnType.STRING;
public Expression.ReturnType getType() {
return Expression.ReturnType.STRING;
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
}
@@ -7,8 +7,8 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public interface Variable<T> {
@@ -16,7 +16,7 @@ public interface Variable<T> {
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;
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BoolAssignmentNode extends VariableAssignmentNode<Boolean> {
public BoolAssignmentNode(Returnable<Boolean> value, Position position, int index) {
public BoolAssignmentNode(Expression<Boolean> value, SourcePosition position, int index) {
super(value, position, index);
}
@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);
}
@@ -1,18 +1,23 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class NumAssignmentNode extends VariableAssignmentNode<Number> {
public NumAssignmentNode(Returnable<Number> value, Position position, int index) {
public NumAssignmentNode(Expression<Number> value, SourcePosition position, int index) {
super(value, position, index);
}
@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);
}
@@ -1,19 +1,24 @@
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class StrAssignmentNode extends VariableAssignmentNode<String> {
public StrAssignmentNode(Returnable<String> value, Position position, int index) {
public StrAssignmentNode(Expression<String> value, SourcePosition position, int index) {
super(value, position, index);
}
@Override
public String apply(ImplementationArguments implementationArguments, Scope scope) {
String val = value.apply(implementationArguments, scope);
public ReturnType returnType() {
return ReturnType.STRING;
}
@Override
public String evaluate(ImplementationArguments implementationArguments, Scope scope) {
String val = value.evaluate(implementationArguments, scope);
scope.setStr(index, val);
return val;
}
@@ -7,25 +7,24 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables.assign;
import com.dfsek.terra.addons.terrascript.parser.lang.Item;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
import com.dfsek.terra.addons.terrascript.parser.lang.Expression;
public abstract class VariableAssignmentNode<T> implements Item<T> {
protected final Returnable<T> value;
public abstract class VariableAssignmentNode<T> implements Expression<T> {
protected final Expression<T> value;
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.index = index;
this.position = position;
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
}
@@ -1,17 +1,17 @@
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
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);
}
@Override
public Boolean apply(ImplementationArguments implementationArguments, Scope scope) {
public Boolean evaluate(ImplementationArguments implementationArguments, Scope scope) {
return scope.getBool(index);
}
@@ -1,17 +1,17 @@
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
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);
}
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return scope.getNum(index);
}
@@ -1,17 +1,17 @@
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.Scope;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
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);
}
@Override
public String apply(ImplementationArguments implementationArguments, Scope scope) {
public String evaluate(ImplementationArguments implementationArguments, Scope scope) {
return scope.getStr(index);
}
}
@@ -7,16 +7,16 @@
package com.dfsek.terra.addons.terrascript.parser.lang.variables.reference;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
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;
private final Position position;
private final SourcePosition position;
private final ReturnType type;
public VariableReferenceNode(Position position, ReturnType type, int index) {
public VariableReferenceNode(SourcePosition position, ReturnType type, int index) {
this.position = position;
this.type = type;
this.index = index;
@@ -28,7 +28,7 @@ public abstract class VariableReferenceNode<T> implements Returnable<T> {
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
}
@@ -7,6 +7,8 @@
package com.dfsek.terra.addons.terrascript.script;
import com.dfsek.terra.addons.terrascript.lexer.Lexer;
import net.jafama.FastMath;
import org.apache.commons.io.IOUtils;
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.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.script.builders.BinaryNumberFunctionBuilder;
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> {
private static final Logger LOGGER = LoggerFactory.getLogger(StructureScript.class);
private final Executable block;
private final Executable executable;
private final RegistryKey id;
private final String profile;
private final Platform platform;
@SuppressWarnings("rawtypes")
public StructureScript(InputStream inputStream, RegistryKey id, Platform platform, Registry<Structure> registry,
public StructureScript(InputStream source, RegistryKey id, Platform platform, Registry<Structure> structureRegistry,
Registry<LootTable> lootRegistry,
Registry<FunctionBuilder> functionRegistry) {
Parser parser;
Lexer lexer;
try {
parser = new Parser(IOUtils.toString(inputStream, Charset.defaultCharset()));
lexer = new Lexer(IOUtils.toString(source, Charset.defaultCharset()));
} catch(IOException e) {
throw new RuntimeException(e);
}
Parser parser = new Parser(lexer);
this.id = id;
this.profile = "terrascript_direct:" + id;
//noinspection unchecked
functionRegistry.forEach((key, function) -> parser.registerFunction(key.getID(), function)); // Register registry functions.
ScopeBuilder scope = new ScopeBuilder();
parser
functionRegistry.forEach((key, function) -> scope.registerFunction(key.getID(), function)); // Register registry functions.
scope
.registerFunction("block", 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("recursions", new RecursionsFunctionBuilder())
.registerFunction("setMark", new SetMarkFunctionBuilder())
@@ -89,15 +94,15 @@ public class StructureScript implements Structure, Keyed<StructureScript> {
.registerFunction("state", new StateFunctionBuilder(platform))
.registerFunction("setWaterlog", new UnaryBooleanFunctionBuilder((waterlog, args) -> args.setWaterlog(waterlog)))
.registerFunction("originX", new ZeroArgFunctionBuilder<Number>(arguments -> arguments.getOrigin().getX(),
Returnable.ReturnType.NUMBER))
Expression.ReturnType.NUMBER))
.registerFunction("originY", new ZeroArgFunctionBuilder<Number>(arguments -> arguments.getOrigin().getY(),
Returnable.ReturnType.NUMBER))
Expression.ReturnType.NUMBER))
.registerFunction("originZ", new ZeroArgFunctionBuilder<Number>(arguments -> arguments.getOrigin().getZ(),
Returnable.ReturnType.NUMBER))
Expression.ReturnType.NUMBER))
.registerFunction("rotation", new ZeroArgFunctionBuilder<>(arguments -> arguments.getRotation().toString(),
Returnable.ReturnType.STRING))
Expression.ReturnType.STRING))
.registerFunction("rotationDegrees", new ZeroArgFunctionBuilder<>(arguments -> arguments.getRotation().getDegrees(),
Returnable.ReturnType.NUMBER))
Expression.ReturnType.NUMBER))
.registerFunction("print",
new UnaryStringFunctionBuilder(string -> LOGGER.info("[TerraScript:{}] {}", id, string)))
.registerFunction("abs", new UnaryNumberFunctionBuilder(number -> FastMath.abs(number.doubleValue())))
@@ -120,11 +125,7 @@ public class StructureScript implements Structure, Keyed<StructureScript> {
.registerFunction("min", new BinaryNumberFunctionBuilder(
(number, number2) -> FastMath.min(number.doubleValue(), number2.doubleValue())));
if(!platform.getTerraConfig().isDebugScript()) {
parser.ignoreFunction("debugBlock");
}
block = parser.parse();
executable = parser.parse(scope);
this.platform = platform;
}
@@ -132,21 +133,21 @@ public class StructureScript implements Structure, Keyed<StructureScript> {
@SuppressWarnings("try")
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation) {
platform.getProfiler().push(profile);
boolean result = applyBlock(new TerraImplementationArguments(location, rotation, random, world, 0));
boolean result = execute(new TerraImplementationArguments(location, rotation, random, world, 0));
platform.getProfiler().pop(profile);
return result;
}
public boolean generate(Vector3Int location, WritableWorld world, Random random, Rotation rotation, int recursions) {
platform.getProfiler().push(profile);
boolean result = applyBlock(new TerraImplementationArguments(location, rotation, random, world, recursions));
boolean result = execute(new TerraImplementationArguments(location, rotation, random, world, recursions));
platform.getProfiler().pop(profile);
return result;
}
private boolean applyBlock(TerraImplementationArguments arguments) {
private boolean execute(TerraImplementationArguments arguments) {
try {
return block.execute(arguments);
return executable.execute(arguments);
} catch(RuntimeException e) {
LOGGER.error("Failed to generate structure at {}", arguments.getOrigin(), e);
return false;
@@ -10,12 +10,12 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class BinaryNumberFunctionBuilder implements FunctionBuilder<Function<Number>> {
@@ -27,7 +27,7 @@ public class BinaryNumberFunctionBuilder implements FunctionBuilder<Function<Num
}
@Override
public Function<Number> build(List<Returnable<?>> argumentList, Position position) {
public Function<Number> build(List<Expression<?>> argumentList, SourcePosition position) {
return new Function<>() {
@Override
public ReturnType returnType() {
@@ -36,13 +36,13 @@ public class BinaryNumberFunctionBuilder implements FunctionBuilder<Function<Num
@SuppressWarnings("unchecked")
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
return function.apply(((Returnable<Number>) argumentList.get(0)).apply(implementationArguments, scope),
((Returnable<Number>) argumentList.get(1)).apply(implementationArguments, scope));
public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return function.apply(((Expression<Number>) argumentList.get(0)).evaluate(implementationArguments, scope),
((Expression<Number>) argumentList.get(1)).evaluate(implementationArguments, scope));
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
};
@@ -54,8 +54,8 @@ public class BinaryNumberFunctionBuilder implements FunctionBuilder<Function<Num
}
@Override
public Returnable.ReturnType getArgument(int position) {
if(position == 0 || position == 1) return Returnable.ReturnType.NUMBER;
public Expression.ReturnType getArgument(int position) {
if(position == 0 || position == 1) return Expression.ReturnType.NUMBER;
return null;
}
}
@@ -9,10 +9,10 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.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.script.functions.BiomeFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform;
@@ -25,9 +25,9 @@ public class BiomeFunctionBuilder implements FunctionBuilder<BiomeFunction> {
@SuppressWarnings("unchecked")
@Override
public BiomeFunction build(List<Returnable<?>> argumentList, Position position) {
return new BiomeFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), position);
public BiomeFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new BiomeFunction((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Expression<Number>) argumentList.get(2), position);
}
@Override
@@ -36,9 +36,9 @@ public class BiomeFunctionBuilder implements FunctionBuilder<BiomeFunction> {
}
@Override
public Returnable.ReturnType getArgument(int position) {
public Expression.ReturnType getArgument(int position) {
return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER;
case 0, 1, 2 -> Expression.ReturnType.NUMBER;
default -> null;
};
}
@@ -9,13 +9,13 @@ package com.dfsek.terra.addons.terrascript.script.builders;
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.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.StringConstant;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder;
import com.dfsek.terra.addons.terrascript.script.functions.BlockFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform;
@@ -28,19 +28,17 @@ public class BlockFunctionBuilder implements FunctionBuilder<BlockFunction> {
@SuppressWarnings("unchecked")
@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);
Returnable<Boolean> overwrite = new BooleanConstant(true, position);
if(argumentList.size() >= 5) overwrite = (Returnable<Boolean>) argumentList.get(4);
Returnable<Boolean> physics = new BooleanConstant(false, position);
if(argumentList.size() == 6) physics = (Returnable<Boolean>) argumentList.get(5);
Expression<Boolean> booleanReturnable = new BooleanConstant(true, position);
if(argumentList.size() == 5) booleanReturnable = (Expression<Boolean>) argumentList.get(4);
if(argumentList.get(3) instanceof StringConstant) {
return new BlockFunction.Constant((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (StringConstant) argumentList.get(3),
overwrite, physics, platform, position);
return new BlockFunction.Constant((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Expression<Number>) argumentList.get(2), (StringConstant) argumentList.get(3),
booleanReturnable, platform, position);
}
return new BlockFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) argumentList.get(3), overwrite, physics,
return new BlockFunction((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Expression<Number>) argumentList.get(2), (Expression<String>) argumentList.get(3), booleanReturnable,
platform, position);
}
@@ -50,11 +48,11 @@ public class BlockFunctionBuilder implements FunctionBuilder<BlockFunction> {
}
@Override
public Returnable.ReturnType getArgument(int position) {
public Expression.ReturnType getArgument(int position) {
return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER;
case 3 -> Returnable.ReturnType.STRING;
case 4, 5 -> Returnable.ReturnType.BOOLEAN;
case 0, 1, 2 -> Expression.ReturnType.NUMBER;
case 3 -> Expression.ReturnType.STRING;
case 4 -> Expression.ReturnType.BOOLEAN;
default -> null;
};
}
@@ -9,18 +9,18 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.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.script.functions.CheckBlockFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class CheckBlockFunctionBuilder implements FunctionBuilder<CheckBlockFunction> {
@SuppressWarnings("unchecked")
@Override
public CheckBlockFunction build(List<Returnable<?>> argumentList, Position position) {
return new CheckBlockFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), position);
public CheckBlockFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new CheckBlockFunction((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Expression<Number>) argumentList.get(2), position);
}
@Override
@@ -29,9 +29,9 @@ public class CheckBlockFunctionBuilder implements FunctionBuilder<CheckBlockFunc
}
@Override
public Returnable.ReturnType getArgument(int position) {
public Expression.ReturnType getArgument(int position) {
return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER;
case 0, 1, 2 -> Expression.ReturnType.NUMBER;
default -> null;
};
}
@@ -9,10 +9,10 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.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.script.functions.EntityFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform;
@@ -25,9 +25,9 @@ public class EntityFunctionBuilder implements FunctionBuilder<EntityFunction> {
@SuppressWarnings("unchecked")
@Override
public EntityFunction build(List<Returnable<?>> argumentList, Position position) {
return new EntityFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) argumentList.get(3), platform, position);
public EntityFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new EntityFunction((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Expression<Number>) argumentList.get(2), (Expression<String>) argumentList.get(3), platform, position);
}
@Override
@@ -36,10 +36,10 @@ public class EntityFunctionBuilder implements FunctionBuilder<EntityFunction> {
}
@Override
public Returnable.ReturnType getArgument(int position) {
public Expression.ReturnType getArgument(int position) {
return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER;
case 3 -> Returnable.ReturnType.STRING;
case 0, 1, 2 -> Expression.ReturnType.NUMBER;
case 3 -> Expression.ReturnType.STRING;
default -> null;
};
}
@@ -9,10 +9,10 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.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.script.functions.GetMarkFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class GetMarkFunctionBuilder implements FunctionBuilder<GetMarkFunction> {
@@ -22,9 +22,9 @@ public class GetMarkFunctionBuilder implements FunctionBuilder<GetMarkFunction>
@SuppressWarnings("unchecked")
@Override
public GetMarkFunction build(List<Returnable<?>> argumentList, Position position) {
return new GetMarkFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), position);
public GetMarkFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new GetMarkFunction((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Expression<Number>) argumentList.get(2), position);
}
@Override
@@ -33,9 +33,9 @@ public class GetMarkFunctionBuilder implements FunctionBuilder<GetMarkFunction>
}
@Override
public Returnable.ReturnType getArgument(int position) {
public Expression.ReturnType getArgument(int position) {
return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER;
case 0, 1, 2 -> Expression.ReturnType.NUMBER;
default -> null;
};
}
@@ -9,11 +9,11 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.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.script.StructureScript;
import com.dfsek.terra.addons.terrascript.script.functions.LootFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.structure.LootTable;
@@ -32,9 +32,9 @@ public class LootFunctionBuilder implements FunctionBuilder<LootFunction> {
@SuppressWarnings("unchecked")
@Override
public LootFunction build(List<Returnable<?>> argumentList, Position position) {
return new LootFunction(registry, (Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) argumentList.get(3), platform, position,
public LootFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new LootFunction(registry, (Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Expression<Number>) argumentList.get(2), (Expression<String>) argumentList.get(3), platform, position,
script);
}
@@ -44,10 +44,10 @@ public class LootFunctionBuilder implements FunctionBuilder<LootFunction> {
}
@Override
public Returnable.ReturnType getArgument(int position) {
public Expression.ReturnType getArgument(int position) {
return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER;
case 3 -> Returnable.ReturnType.STRING;
case 0, 1, 2 -> Expression.ReturnType.NUMBER;
case 3 -> Expression.ReturnType.STRING;
default -> null;
};
}
@@ -9,10 +9,10 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.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.script.functions.PullFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform;
@@ -25,9 +25,9 @@ public class PullFunctionBuilder implements FunctionBuilder<PullFunction> {
@SuppressWarnings("unchecked")
@Override
public PullFunction build(List<Returnable<?>> argumentList, Position position) {
return new PullFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) argumentList.get(3), platform, position);
public PullFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new PullFunction((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Expression<Number>) argumentList.get(2), (Expression<String>) argumentList.get(3), platform, position);
}
@Override
@@ -36,10 +36,10 @@ public class PullFunctionBuilder implements FunctionBuilder<PullFunction> {
}
@Override
public Returnable.ReturnType getArgument(int position) {
public Expression.ReturnType getArgument(int position) {
return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER;
case 3 -> Returnable.ReturnType.STRING;
case 0, 1, 2 -> Expression.ReturnType.NUMBER;
case 3 -> Expression.ReturnType.STRING;
default -> null;
};
}
@@ -9,17 +9,17 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.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.script.functions.RandomFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class RandomFunctionBuilder implements FunctionBuilder<RandomFunction> {
@SuppressWarnings("unchecked")
@Override
public RandomFunction build(List<Returnable<?>> argumentList, Position position) {
return new RandomFunction((Returnable<Number>) argumentList.get(0), position);
public RandomFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new RandomFunction((Expression<Number>) argumentList.get(0), position);
}
@Override
@@ -28,8 +28,8 @@ public class RandomFunctionBuilder implements FunctionBuilder<RandomFunction> {
}
@Override
public Returnable.ReturnType getArgument(int position) {
if(position == 0) return Returnable.ReturnType.NUMBER;
public Expression.ReturnType getArgument(int position) {
if(position == 0) return Expression.ReturnType.NUMBER;
return null;
}
}
@@ -9,15 +9,15 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.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.script.functions.RecursionsFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class RecursionsFunctionBuilder implements FunctionBuilder<RecursionsFunction> {
@Override
public RecursionsFunction build(List<Returnable<?>> argumentList, Position position) {
public RecursionsFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new RecursionsFunction(position);
}
@@ -27,7 +27,7 @@ public class RecursionsFunctionBuilder implements FunctionBuilder<RecursionsFunc
}
@Override
public Returnable.ReturnType getArgument(int position) {
public Expression.ReturnType getArgument(int position) {
return null;
}
}
@@ -9,10 +9,10 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.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.script.functions.SetMarkFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class SetMarkFunctionBuilder implements FunctionBuilder<SetMarkFunction> {
@@ -22,9 +22,9 @@ public class SetMarkFunctionBuilder implements FunctionBuilder<SetMarkFunction>
@SuppressWarnings("unchecked")
@Override
public SetMarkFunction build(List<Returnable<?>> argumentList, Position position) {
return new SetMarkFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) argumentList.get(3), position);
public SetMarkFunction build(List<Expression<?>> argumentList, SourcePosition position) {
return new SetMarkFunction((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Expression<Number>) argumentList.get(2), (Expression<String>) argumentList.get(3), position);
}
@Override
@@ -33,10 +33,10 @@ public class SetMarkFunctionBuilder implements FunctionBuilder<SetMarkFunction>
}
@Override
public Returnable.ReturnType getArgument(int position) {
public Expression.ReturnType getArgument(int position) {
return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER;
case 3 -> Returnable.ReturnType.STRING;
case 0, 1, 2 -> Expression.ReturnType.NUMBER;
case 3 -> Expression.ReturnType.STRING;
default -> null;
};
}
@@ -9,11 +9,11 @@ package com.dfsek.terra.addons.terrascript.script.builders;
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.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.script.functions.StateFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform;
@@ -26,10 +26,10 @@ public class StateFunctionBuilder implements FunctionBuilder<StateFunction> {
@SuppressWarnings("unchecked")
@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);
return new StateFunction((Returnable<Number>) argumentList.get(0), (Returnable<Number>) argumentList.get(1),
(Returnable<Number>) argumentList.get(2), (Returnable<String>) argumentList.get(3), position);
return new StateFunction((Expression<Number>) argumentList.get(0), (Expression<Number>) argumentList.get(1),
(Expression<Number>) argumentList.get(2), (Expression<String>) argumentList.get(3), position);
}
@Override
@@ -38,10 +38,10 @@ public class StateFunctionBuilder implements FunctionBuilder<StateFunction> {
}
@Override
public Returnable.ReturnType getArgument(int position) {
public Expression.ReturnType getArgument(int position) {
return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER;
case 3 -> Returnable.ReturnType.STRING;
case 0, 1, 2 -> Expression.ReturnType.NUMBER;
case 3 -> Expression.ReturnType.STRING;
default -> null;
};
}
@@ -10,11 +10,11 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
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.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.script.functions.StructureFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.registry.Registry;
import com.dfsek.terra.api.structure.Structure;
@@ -31,12 +31,12 @@ public class StructureFunctionBuilder implements FunctionBuilder<StructureFuncti
@SuppressWarnings("unchecked")
@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);
return new StructureFunction((Returnable<Number>) argumentList.remove(0), (Returnable<Number>) argumentList.remove(0),
(Returnable<Number>) argumentList.remove(0), (Returnable<String>) argumentList.remove(0),
argumentList.stream().map(item -> ((Returnable<String>) item)).collect(Collectors.toList()), registry,
return new StructureFunction((Expression<Number>) argumentList.remove(0), (Expression<Number>) argumentList.remove(0),
(Expression<Number>) argumentList.remove(0), (Expression<String>) argumentList.remove(0),
argumentList.stream().map(item -> ((Expression<String>) item)).collect(Collectors.toList()), registry,
position, platform);
}
@@ -46,10 +46,10 @@ public class StructureFunctionBuilder implements FunctionBuilder<StructureFuncti
}
@Override
public Returnable.ReturnType getArgument(int position) {
public Expression.ReturnType getArgument(int position) {
return switch(position) {
case 0, 1, 2 -> Returnable.ReturnType.NUMBER;
default -> Returnable.ReturnType.STRING;
case 0, 1, 2 -> Expression.ReturnType.NUMBER;
default -> Expression.ReturnType.STRING;
};
}
}
@@ -10,13 +10,13 @@ package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class UnaryBooleanFunctionBuilder implements FunctionBuilder<Function<Void>> {
@@ -28,7 +28,7 @@ public class UnaryBooleanFunctionBuilder implements FunctionBuilder<Function<Voi
}
@Override
public Function<Void> build(List<Returnable<?>> argumentList, Position position) {
public Function<Void> build(List<Expression<?>> argumentList, SourcePosition position) {
return new Function<>() {
@Override
public ReturnType returnType() {
@@ -37,14 +37,14 @@ public class UnaryBooleanFunctionBuilder implements FunctionBuilder<Function<Voi
@SuppressWarnings("unchecked")
@Override
public Void apply(ImplementationArguments implementationArguments, Scope scope) {
function.accept(((Returnable<Boolean>) argumentList.get(0)).apply(implementationArguments, scope),
public Void evaluate(ImplementationArguments implementationArguments, Scope scope) {
function.accept(((Expression<Boolean>) argumentList.get(0)).evaluate(implementationArguments, scope),
(TerraImplementationArguments) implementationArguments);
return null;
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
};
@@ -56,8 +56,8 @@ public class UnaryBooleanFunctionBuilder implements FunctionBuilder<Function<Voi
}
@Override
public Returnable.ReturnType getArgument(int position) {
if(position == 0) return Returnable.ReturnType.BOOLEAN;
public Expression.ReturnType getArgument(int position) {
if(position == 0) return Expression.ReturnType.BOOLEAN;
return null;
}
}
@@ -9,12 +9,12 @@ package com.dfsek.terra.addons.terrascript.script.builders;
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class UnaryNumberFunctionBuilder implements FunctionBuilder<Function<Number>> {
@@ -26,7 +26,7 @@ public class UnaryNumberFunctionBuilder implements FunctionBuilder<Function<Numb
}
@Override
public Function<Number> build(List<Returnable<?>> argumentList, Position position) {
public Function<Number> build(List<Expression<?>> argumentList, SourcePosition position) {
return new Function<>() {
@Override
public ReturnType returnType() {
@@ -35,12 +35,12 @@ public class UnaryNumberFunctionBuilder implements FunctionBuilder<Function<Numb
@SuppressWarnings("unchecked")
@Override
public Number apply(ImplementationArguments implementationArguments, Scope scope) {
return function.apply(((Returnable<Number>) argumentList.get(0)).apply(implementationArguments, scope));
public Number evaluate(ImplementationArguments implementationArguments, Scope scope) {
return function.apply(((Expression<Number>) argumentList.get(0)).evaluate(implementationArguments, scope));
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
};
@@ -52,8 +52,8 @@ public class UnaryNumberFunctionBuilder implements FunctionBuilder<Function<Numb
}
@Override
public Returnable.ReturnType getArgument(int position) {
if(position == 0) return Returnable.ReturnType.NUMBER;
public Expression.ReturnType getArgument(int position) {
if(position == 0) return Expression.ReturnType.NUMBER;
return null;
}
}
@@ -9,12 +9,12 @@ package com.dfsek.terra.addons.terrascript.script.builders;
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class UnaryStringFunctionBuilder implements FunctionBuilder<Function<Void>> {
@@ -26,7 +26,7 @@ public class UnaryStringFunctionBuilder implements FunctionBuilder<Function<Void
}
@Override
public Function<Void> build(List<Returnable<?>> argumentList, Position position) {
public Function<Void> build(List<Expression<?>> argumentList, SourcePosition position) {
return new Function<>() {
@Override
public ReturnType returnType() {
@@ -35,13 +35,13 @@ public class UnaryStringFunctionBuilder implements FunctionBuilder<Function<Void
@SuppressWarnings("unchecked")
@Override
public Void apply(ImplementationArguments implementationArguments, Scope scope) {
function.accept(((Returnable<String>) argumentList.get(0)).apply(implementationArguments, scope));
public Void evaluate(ImplementationArguments implementationArguments, Scope scope) {
function.accept(((Expression<String>) argumentList.get(0)).evaluate(implementationArguments, scope));
return null;
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
};
@@ -53,8 +53,8 @@ public class UnaryStringFunctionBuilder implements FunctionBuilder<Function<Void
}
@Override
public Returnable.ReturnType getArgument(int position) {
if(position == 0) return Returnable.ReturnType.STRING;
public Expression.ReturnType getArgument(int position) {
if(position == 0) return Expression.ReturnType.STRING;
return null;
}
}
@@ -9,26 +9,26 @@ package com.dfsek.terra.addons.terrascript.script.builders;
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
public class ZeroArgFunctionBuilder<T> implements FunctionBuilder<Function<T>> {
private final java.util.function.Function<TerraImplementationArguments, T> function;
private final Returnable.ReturnType type;
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.type = type;
}
@Override
public Function<T> build(List<Returnable<?>> argumentList, Position position) {
public Function<T> build(List<Expression<?>> argumentList, SourcePosition position) {
return new Function<>() {
@Override
public ReturnType returnType() {
@@ -36,12 +36,12 @@ public class ZeroArgFunctionBuilder<T> implements FunctionBuilder<Function<T>> {
}
@Override
public T apply(ImplementationArguments implementationArguments, Scope scope) {
public T evaluate(ImplementationArguments implementationArguments, Scope scope) {
return function.apply((TerraImplementationArguments) implementationArguments);
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
};
@@ -53,7 +53,7 @@ public class ZeroArgFunctionBuilder<T> implements FunctionBuilder<Function<T>> {
}
@Override
public Returnable.ReturnType getArgument(int position) {
public Expression.ReturnType getArgument(int position) {
if(position == 0) return type;
return null;
}
@@ -9,12 +9,12 @@ package com.dfsek.terra.addons.terrascript.script.functions;
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.util.RotationUtil;
import com.dfsek.terra.api.util.vector.Vector2;
import com.dfsek.terra.api.util.vector.Vector3;
@@ -22,11 +22,11 @@ import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class BiomeFunction implements Function<String> {
private final Returnable<Number> x, y, z;
private final Position position;
private final Expression<Number> x, y, z;
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.y = y;
this.z = z;
@@ -35,11 +35,11 @@ public class BiomeFunction implements Function<String> {
@Override
public String apply(ImplementationArguments implementationArguments, Scope scope) {
public String evaluate(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()),
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.evaluate(implementationArguments, scope).doubleValue(),
z.evaluate(implementationArguments, scope).doubleValue()),
arguments.getRotation());
@@ -49,12 +49,12 @@ public class BiomeFunction implements Function<String> {
.toVector3()
.mutable()
.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();
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
@@ -14,13 +14,13 @@ import org.slf4j.LoggerFactory;
import java.util.HashMap;
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.constants.StringConstant;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.util.RotationUtil;
@@ -30,16 +30,15 @@ import com.dfsek.terra.api.util.vector.Vector3;
public class BlockFunction implements Function<Void> {
private static final Logger logger = LoggerFactory.getLogger(BlockFunction.class);
protected final Returnable<Number> x, y, z;
protected final Returnable<String> blockData;
protected final Expression<Number> x, y, z;
protected final Expression<String> blockData;
protected final Platform platform;
private final Map<String, BlockState> data = new HashMap<>();
private final Returnable<Boolean> overwrite;
private final Returnable<Boolean> physics;
private final Position position;
private final Expression<Boolean> overwrite;
private final SourcePosition position;
public BlockFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Returnable<String> blockData,
Returnable<Boolean> overwrite, Returnable<Boolean> physics, Platform platform, Position position) {
public BlockFunction(Expression<Number> x, Expression<Number> y, Expression<Number> z, Expression<String> blockData,
Expression<Boolean> overwrite, Platform platform, SourcePosition position) {
this.x = x;
this.y = y;
this.z = z;
@@ -47,11 +46,10 @@ public class BlockFunction implements Function<Void> {
this.overwrite = overwrite;
this.platform = platform;
this.position = position;
this.physics = physics;
}
@Override
public Void apply(ImplementationArguments implementationArguments, Scope scope) {
public Void evaluate(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
BlockState rot = getBlockState(implementationArguments, scope);
setBlock(implementationArguments, scope, arguments, rot);
@@ -59,7 +57,7 @@ public class BlockFunction implements Function<Void> {
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
@@ -70,15 +68,16 @@ public class BlockFunction implements Function<Void> {
void setBlock(ImplementationArguments implementationArguments, Scope scope,
TerraImplementationArguments arguments, BlockState rot) {
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()), arguments.getRotation());
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.evaluate(implementationArguments, scope).doubleValue(),
z.evaluate(implementationArguments, scope).doubleValue()),
arguments.getRotation());
try {
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());
BlockState current = arguments.getWorld().getBlockState(set);
if(overwrite.apply(implementationArguments, scope) || current.isAir()) {
arguments.getWorld().setBlockState(set, rot, physics.apply(implementationArguments, scope));
if(overwrite.evaluate(implementationArguments, scope) || current.isAir()) {
arguments.getWorld().setBlockState(set, rot);
}
} catch(RuntimeException e) {
logger.error("Failed to place block at location {}", arguments.getOrigin(), e);
@@ -86,16 +85,16 @@ public class BlockFunction implements Function<Void> {
}
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 {
private final BlockState state;
public Constant(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, StringConstant blockData,
Returnable<Boolean> overwrite, Returnable<Boolean> physics, Platform platform, Position position) {
super(x, y, z, blockData, overwrite, physics, platform, position);
public Constant(Expression<Number> x, Expression<Number> y, Expression<Number> z, StringConstant blockData,
Expression<Boolean> overwrite, Platform platform, SourcePosition position) {
super(x, y, z, blockData, overwrite, platform, position);
this.state = platform.getWorldHandle().createBlockState(blockData.getConstant());
}
@@ -9,22 +9,22 @@ package com.dfsek.terra.addons.terrascript.script.functions;
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.util.RotationUtil;
import com.dfsek.terra.api.util.vector.Vector2;
import com.dfsek.terra.api.util.vector.Vector3;
public class CheckBlockFunction implements Function<String> {
private final Returnable<Number> x, y, z;
private final Position position;
private final Expression<Number> x, y, z;
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.y = y;
this.z = z;
@@ -33,11 +33,11 @@ public class CheckBlockFunction implements Function<String> {
@Override
public String apply(ImplementationArguments implementationArguments, Scope scope) {
public String evaluate(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()),
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.evaluate(implementationArguments, scope).doubleValue(),
z.evaluate(implementationArguments, scope).doubleValue()),
arguments.getRotation());
@@ -46,7 +46,7 @@ public class CheckBlockFunction implements Function<String> {
.toVector3()
.mutable()
.add(Vector3.of(FastMath.roundToInt(xz.getX()),
y.apply(implementationArguments, scope)
y.evaluate(implementationArguments, scope)
.doubleValue(), FastMath.roundToInt(xz.getZ()))))
.getAsString();
if(data.contains("[")) return data.substring(0, data.indexOf('[')); // Strip properties
@@ -54,7 +54,7 @@ public class CheckBlockFunction implements Function<String> {
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
@@ -7,14 +7,14 @@
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.lang.Expression;
import com.dfsek.terra.addons.terrascript.parser.lang.ImplementationArguments;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.constants.ConstantExpression;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.entity.Entity;
import com.dfsek.terra.api.entity.EntityType;
@@ -26,12 +26,12 @@ import com.dfsek.terra.api.util.vector.Vector3;
public class EntityFunction implements Function<Void> {
private final EntityType data;
private final Returnable<Number> x, y, z;
private final Position position;
private final Expression<Number> x, y, z;
private final SourcePosition position;
private final Platform platform;
public EntityFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Returnable<String> data, Platform platform,
Position position) {
public EntityFunction(Expression<Number> x, Expression<Number> y, Expression<Number> z, Expression<String> data, Platform platform,
SourcePosition position) {
this.position = position;
this.platform = platform;
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
public Void apply(ImplementationArguments implementationArguments, Scope scope) {
public Void evaluate(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()), arguments.getRotation());
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.evaluate(implementationArguments, scope).doubleValue(),
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())
.mutable()
.add(arguments.getOrigin())
@@ -59,7 +60,7 @@ public class EntityFunction implements Function<Void> {
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
@@ -9,22 +9,22 @@ package com.dfsek.terra.addons.terrascript.script.functions;
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.util.RotationUtil;
import com.dfsek.terra.api.util.vector.Vector2;
import com.dfsek.terra.api.util.vector.Vector3;
public class GetMarkFunction implements Function<String> {
private final Returnable<Number> x, y, z;
private final Position position;
private final Expression<Number> x, y, z;
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.x = x;
this.y = y;
@@ -32,13 +32,14 @@ public class GetMarkFunction implements Function<String> {
}
@Override
public String apply(ImplementationArguments implementationArguments, Scope scope) {
public String evaluate(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()), arguments.getRotation());
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.evaluate(implementationArguments, scope).doubleValue(),
z.evaluate(implementationArguments, scope).doubleValue()),
arguments.getRotation());
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()))
.mutable()
.add(arguments.getOrigin())
@@ -47,7 +48,7 @@ public class GetMarkFunction implements Function<String> {
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
@@ -13,13 +13,13 @@ import org.slf4j.LoggerFactory;
import java.util.Random;
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.script.StructureScript;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.block.entity.BlockEntity;
import com.dfsek.terra.api.block.entity.Container;
@@ -35,14 +35,14 @@ import com.dfsek.terra.api.util.vector.Vector3;
public class LootFunction implements Function<Void> {
private static final Logger LOGGER = LoggerFactory.getLogger(LootFunction.class);
private final Registry<LootTable> registry;
private final Returnable<String> data;
private final Returnable<Number> x, y, z;
private final Position position;
private final Expression<String> data;
private final Expression<Number> x, y, z;
private final SourcePosition position;
private final Platform platform;
private final StructureScript script;
public LootFunction(Registry<LootTable> registry, Returnable<Number> x, Returnable<Number> y, Returnable<Number> z,
Returnable<String> data, Platform platform, Position position, StructureScript script) {
public LootFunction(Registry<LootTable> registry, Expression<Number> x, Expression<Number> y, Expression<Number> z,
Expression<String> data, Platform platform, SourcePosition position, StructureScript script) {
this.registry = registry;
this.position = position;
this.data = data;
@@ -54,23 +54,23 @@ public class LootFunction implements Function<Void> {
}
@Override
public Void apply(ImplementationArguments implementationArguments, Scope scope) {
public Void evaluate(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()),
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.evaluate(implementationArguments, scope).doubleValue(),
z.evaluate(implementationArguments, scope).doubleValue()),
arguments.getRotation());
String id = data.apply(implementationArguments, scope);
String id = data.evaluate(implementationArguments, scope);
registry.get(RegistryKey.parse(id))
.ifPresentOrElse(table -> {
Vector3 apply = Vector3.of(FastMath.roundToInt(xz.getX()),
y.apply(implementationArguments, scope)
y.evaluate(implementationArguments, scope)
.intValue(),
FastMath.roundToInt(xz.getZ())).mutable().add(arguments.getOrigin()).immutable();
try {
BlockEntity data = arguments.getWorld().getBlockEntity(apply);
if(!(data instanceof Container container)) {
@@ -78,12 +78,12 @@ public class LootFunction implements Function<Void> {
apply, data);
return;
}
LootPopulateEvent event = new LootPopulateEvent(container, table,
arguments.getWorld().getPack(), script);
platform.getEventManager().callEvent(event);
if(event.isCancelled()) return;
event.getTable().fillInventory(container.getInventory(),
new Random(apply.hashCode()));
data.update(false);
@@ -97,7 +97,7 @@ public class LootFunction implements Function<Void> {
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}
@@ -9,14 +9,14 @@ package com.dfsek.terra.addons.terrascript.script.functions;
import net.jafama.FastMath;
import com.dfsek.terra.addons.terrascript.lexer.SourcePosition;
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.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.Scope;
import com.dfsek.terra.addons.terrascript.parser.lang.constants.ConstantExpression;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.Function;
import com.dfsek.terra.addons.terrascript.script.TerraImplementationArguments;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.util.RotationUtil;
@@ -26,11 +26,11 @@ import com.dfsek.terra.api.util.vector.Vector3;
public class PullFunction implements Function<Void> {
private final BlockState data;
private final Returnable<Number> x, y, z;
private final Position position;
private final Expression<Number> x, y, z;
private final SourcePosition position;
public PullFunction(Returnable<Number> x, Returnable<Number> y, Returnable<Number> z, Returnable<String> data, Platform platform,
Position position) {
public PullFunction(Expression<Number> x, Expression<Number> y, Expression<Number> z, Expression<String> data, Platform platform,
SourcePosition position) {
this.position = position;
if(!(data instanceof ConstantExpression)) throw new ParseException("Block data must be constant", data.getPosition());
@@ -41,12 +41,13 @@ public class PullFunction implements Function<Void> {
}
@Override
public Void apply(ImplementationArguments implementationArguments, Scope scope) {
public Void evaluate(ImplementationArguments implementationArguments, Scope scope) {
TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments;
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.apply(implementationArguments, scope).doubleValue(),
z.apply(implementationArguments, scope).doubleValue()), arguments.getRotation());
Vector2 xz = RotationUtil.rotateVector(Vector2.of(x.evaluate(implementationArguments, scope).doubleValue(),
z.evaluate(implementationArguments, scope).doubleValue()),
arguments.getRotation());
Vector3.Mutable mutable = Vector3.of(FastMath.roundToInt(xz.getX()), y.apply(implementationArguments, scope).intValue(),
Vector3.Mutable mutable = Vector3.of(FastMath.roundToInt(xz.getX()), y.evaluate(implementationArguments, scope).intValue(),
FastMath.roundToInt(xz.getZ())).mutable().add(arguments.getOrigin());
while(mutable.getY() > arguments.getWorld().getMinHeight()) {
if(!arguments.getWorld().getBlockState(mutable).isAir()) {
@@ -59,7 +60,7 @@ public class PullFunction implements Function<Void> {
}
@Override
public Position getPosition() {
public SourcePosition getPosition() {
return position;
}

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