Skip to content

Commit ea3a0f6

Browse files
Merge pull request #2 from MarcellPerger1/add-pow
Add PowOperation
2 parents df2396d + e078e4b commit ea3a0f6

File tree

15 files changed

+658
-187
lines changed

15 files changed

+658
-187
lines changed

.devcontainer/devcontainer.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
2+
// README at: https://github.com/devcontainers/templates/tree/main/src/java
3+
{
4+
"name": "Java",
5+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
6+
"image": "mcr.microsoft.com/devcontainers/java:1-21-bullseye",
7+
8+
"features": {
9+
"ghcr.io/devcontainers/features/java:1": {
10+
"version": "none",
11+
"installMaven": "true",
12+
"installGradle": "false"
13+
},
14+
"ghcr.io/devcontainers/features/python:1": {
15+
"installTools": true,
16+
"version": "3.11"
17+
}
18+
}
19+
20+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
21+
// "forwardPorts": [],
22+
23+
// Use 'postCreateCommand' to run commands after the container is created.
24+
// "postCreateCommand": "java -version",
25+
26+
// Configure tool-specific properties.
27+
// "customizations": {},
28+
29+
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
30+
// "remoteUser": "root"
31+
}

.github/dependabot.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# To get started with Dependabot version updates, you'll need to specify which
2+
# package ecosystems to update and where the package manifests are located.
3+
# Please see the documentation for more information:
4+
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5+
# https://containers.dev/guide/dependabot
6+
7+
version: 2
8+
updates:
9+
- package-ecosystem: "devcontainers"
10+
directory: "/"
11+
schedule:
12+
interval: weekly

src/main/java/net/marcellperger/mathexpr/BinOpBiConstructor.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33

44
@FunctionalInterface
5-
public interface BinOpBiConstructor<R extends MathSymbol> {
6-
R construct(MathSymbol left, MathSymbol right);
5+
public interface BinOpBiConstructor {
6+
// NOTE: Not generic, but implementations will just return `MathSymbol`
7+
// (which is allowed to be any subclass of MathSymbol) similar to
8+
// `List<T> of(T... values)` can return any `List` implementation
9+
MathSymbol construct(MathSymbol left, MathSymbol right);
710
}

src/main/java/net/marcellperger/mathexpr/BinaryOperation.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,8 @@ public interface BinaryOperation extends MathSymbol {
88
default String fmt() {
99
return "%s(%s, %s)".formatted(this.getClass().getSimpleName(), getLeft().fmt(), getRight().fmt());
1010
}
11+
12+
static MathSymbol construct(MathSymbol left, SymbolInfo op, MathSymbol right) {
13+
return op.getBiConstructor().construct(left, right);
14+
}
1115
}

src/main/java/net/marcellperger/mathexpr/BinaryOperationLeftRight.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ public MathSymbol getRight() {
3737
return SymbolInfo.infixFromClass(getClass());
3838
}
3939

40+
@Contract(pure = true)
41+
public @Nullable String getSpacesAroundInfixInst() {
42+
return SymbolInfo.spacesAroundInfixFromClass(getClass());
43+
}
44+
4045
@Contract(pure = true)
4146
public @Nullable GroupingDirection getGroupingDirectionInst() {
4247
return SymbolInfo.groupingDirectionFromClass(getClass());
@@ -46,9 +51,11 @@ public MathSymbol getRight() {
4651
public String fmt() {
4752
String infix = getInfixInst();
4853
if(infix == null) return BinaryOperation.super.fmt(); // fallback
49-
return "%s %s %s".formatted(
54+
return "%s%s%s%s%s".formatted(
5055
left.fmtWithParensIfRequired(instPrecedenceInt(), _parensRequiredIfEqual(LeftRight.LEFT)),
56+
getSpacesAroundInfixInst(),
5157
infix,
58+
getSpacesAroundInfixInst(),
5259
right.fmtWithParensIfRequired(instPrecedenceInt(), _parensRequiredIfEqual(LeftRight.RIGHT)));
5360
}
5461

src/main/java/net/marcellperger/mathexpr/MathSymbol.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package net.marcellperger.mathexpr;
22

33

4-
import net.marcellperger.mathexpr.util.Util;
54
import org.jetbrains.annotations.Nullable;
65

76
// TODO maybe make it required to have an entry in SymbolInfo
@@ -19,7 +18,7 @@ public interface MathSymbol {
1918
* just for a nicer syntax when we DO have an instance
2019
*/
2120
default @Nullable Integer instPrecedenceInt() {
22-
return Util.chainNulls(SymbolInfo.fromClass(this.getClass()), s -> s.precedence);
21+
return SymbolInfo.precedenceFromClass(getClass());
2322
}
2423

2524
default String fmtAlwaysParens() {
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package net.marcellperger.mathexpr;
2+
3+
import net.marcellperger.mathexpr.util.Util;
4+
import net.marcellperger.mathexpr.util.UtilCollectors;
5+
import org.jetbrains.annotations.Contract;
6+
import org.jetbrains.annotations.NotNull;
7+
import org.jetbrains.annotations.Nullable;
8+
9+
import java.util.Comparator;
10+
import java.util.List;
11+
import java.util.Map;
12+
import java.util.Set;
13+
import java.util.stream.Collectors;
14+
15+
/**
16+
* Data for a specific precedence level
17+
*/
18+
public class PrecedenceLevelInfo {
19+
public Set<SymbolInfo> symbols;
20+
public Map<String, SymbolInfo> infixToSymbolMap;
21+
public List<String> sortedInfixes;
22+
public @Nullable GroupingDirection dirn;
23+
public int precedence;
24+
25+
/**
26+
* @param precedence_ The precedence level (gets data from SymbolInfo)
27+
*/
28+
public PrecedenceLevelInfo(int precedence_) {
29+
precedence = precedence_;
30+
symbols = SymbolInfo.PREC_TO_INFO_MAP.get(precedence);
31+
// Shouldn't be passed an empty level in the first place
32+
Util.requireNonEmptyNonNull(symbols, "Cannot create PrecedenceLevelInfo for a level with no items");
33+
dirn = symbols.stream()
34+
.map(sm -> sm.groupingDirection).collect(UtilCollectors.singleDistinctItem());
35+
try {
36+
infixToSymbolMap = symbols.stream().collect(
37+
Collectors.toMap(
38+
info -> Util.requireNonNull(info.infix, new NullInfixException()),
39+
info -> info));
40+
} catch (NullInfixException e) {
41+
// a precedence level should really be uniform so if any infix is null,
42+
// we set this to null. This may help avoid any subtle bugs later.
43+
infixToSymbolMap = null;
44+
sortedInfixes = null;
45+
}
46+
if(infixToSymbolMap != null) {
47+
sortedInfixes = infixToSymbolMap.keySet().stream()
48+
.sorted(Comparator.comparingInt(String::length).reversed()).toList();
49+
}
50+
}
51+
52+
@Contract("_ -> new")
53+
public static Map.@NotNull Entry<Integer, PrecedenceLevelInfo> newMapEntry(int precedence) {
54+
return Util.makeEntry(precedence, new PrecedenceLevelInfo(precedence));
55+
}
56+
57+
/** Marker exception that we throw to signal that there should be no infix info at all */
58+
private static class NullInfixException extends RuntimeException {}
59+
}

src/main/java/net/marcellperger/mathexpr/SymbolInfo.java

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package net.marcellperger.mathexpr;
22

33
import net.marcellperger.mathexpr.util.Util;
4+
import net.marcellperger.mathexpr.util.UtilCollectors;
45
import org.jetbrains.annotations.NotNull;
56
import org.jetbrains.annotations.Nullable;
67

@@ -18,37 +19,44 @@
1819

1920
public enum SymbolInfo {
2021
// Let's say that precedence 0 is for (parens) OR literals - TODO add a class?? but it wouldn't actually be used !
21-
// POW(PowOperation.class, 1, GroupingDirection.RightToLeft, "**"),
22-
MUL(MulOperation.class, 1, GroupingDirection.LeftToRight, "*", MulOperation::new),
23-
DIV(DivOperation.class, 1, GroupingDirection.LeftToRight, "/", DivOperation::new),
22+
POW(PowOperation.class, 1, GroupingDirection.RightToLeft, "**", "", PowOperation::new),
2423

25-
ADD(AddOperation.class, 2, GroupingDirection.LeftToRight, "+", AddOperation::new),
26-
SUB(SubOperation.class, 2, GroupingDirection.LeftToRight, "-", SubOperation::new),
24+
MUL(MulOperation.class, 2, GroupingDirection.LeftToRight, "*", " ", MulOperation::new),
25+
DIV(DivOperation.class, 2, GroupingDirection.LeftToRight, "/", " ", DivOperation::new),
26+
27+
ADD(AddOperation.class, 3, GroupingDirection.LeftToRight, "+", " ", AddOperation::new),
28+
SUB(SubOperation.class, 3, GroupingDirection.LeftToRight, "-", " ", SubOperation::new),
2729
;
2830

2931
public static final Map<Class<? extends MathSymbol>, SymbolInfo> CLS_TO_INFO_MAP;
3032
public static final Map<Integer, Set<SymbolInfo>> PREC_TO_INFO_MAP;
3133
public static final List<Entry<Integer, Set<SymbolInfo>>> PREC_SORTED_INFO;
34+
public static final int MAX_PRECEDENCE;
35+
public static final Map<Integer, PrecedenceLevelInfo> PREC_LEVELS_INFO;
3236

3337
public final int precedence; // TODO make this Integer
3438
public final Class<? extends MathSymbol> cls;
3539
public final GroupingDirection groupingDirection;
3640
public final String infix;
41+
public final @Nullable String spacesAroundInfix;
3742

43+
@SuppressWarnings("unused") // useful later
3844
SymbolInfo(Class<? extends MathSymbol> cls, int precedence,
39-
GroupingDirection groupingDirection, @Nullable String infix) {
45+
GroupingDirection groupingDirection, @Nullable String spacesAroundInfix, @Nullable String infix) {
4046
this.precedence = precedence;
41-
this.cls = cls; // TODO: private + getters?
47+
this.cls = cls;
4248
this.groupingDirection = groupingDirection;
4349
this.infix = infix;
50+
this.spacesAroundInfix = spacesAroundInfix;
4451
}
4552
SymbolInfo(Class<? extends BinaryOperationLeftRight> cls, int precedence,
46-
GroupingDirection groupingDirection, @Nullable String infix,
47-
BinOpBiConstructor<?> biConstructor) {
53+
GroupingDirection groupingDirection, @Nullable String infix, @Nullable String spacesAroundInfix,
54+
BinOpBiConstructor biConstructor) {
4855
this.precedence = precedence;
4956
this.cls = cls; // TODO: private + getters?
5057
this.groupingDirection = groupingDirection;
5158
this.infix = infix;
59+
this.spacesAroundInfix = spacesAroundInfix;
5260
this.biConstructorCache = biConstructor;
5361
}
5462

@@ -66,11 +74,13 @@ public enum SymbolInfo {
6674
public static @Nullable String infixFromClass(Class<? extends MathSymbol> cls) {
6775
return Util.chainNulls(fromClass(cls), x -> x.infix);
6876
}
77+
public static @Nullable String spacesAroundInfixFromClass(Class<? extends MathSymbol> cls) {
78+
return Util.chainNulls(fromClass(cls), s -> s.spacesAroundInfix);
79+
}
6980

70-
private BinOpBiConstructor<?> biConstructorCache = null;
71-
public @NotNull BinOpBiConstructor<?> getBiConstructor() {
81+
private BinOpBiConstructor biConstructorCache = null;
82+
public @NotNull BinOpBiConstructor getBiConstructor() {
7283
if(biConstructorCache != null) return biConstructorCache;
73-
// TODO could add a BiFunction<> arg to SymbolInfo(), then pass AddOperation::new etc.
7484
assert BinaryOperationLeftRight.class.isAssignableFrom(cls);
7585

7686
Constructor<? extends BinaryOperationLeftRight> ctor;
@@ -104,5 +114,7 @@ public enum SymbolInfo {
104114
CLS_TO_INFO_MAP = Arrays.stream(values()).collect(Collectors.toUnmodifiableMap(p -> p.cls, p -> p));
105115
PREC_TO_INFO_MAP = Arrays.stream(values()).collect(Collectors.groupingBy(s -> s.precedence, Collectors.toUnmodifiableSet()));
106116
PREC_SORTED_INFO = PREC_TO_INFO_MAP.entrySet().stream().sorted(Comparator.comparingInt(Entry::getKey)).toList();
117+
MAX_PRECEDENCE = PREC_SORTED_INFO.getLast().getKey();
118+
PREC_LEVELS_INFO = PREC_TO_INFO_MAP.keySet().stream().map(PrecedenceLevelInfo::newMapEntry).collect(UtilCollectors.entriesToMap());
107119
}
108120
}

0 commit comments

Comments
 (0)