diff --git a/src/main/java/org/nwapw/abacus/Abacus.java b/src/main/java/org/nwapw/abacus/Abacus.java index ff649b5..1ee8f8a 100644 --- a/src/main/java/org/nwapw/abacus/Abacus.java +++ b/src/main/java/org/nwapw/abacus/Abacus.java @@ -57,7 +57,7 @@ public class Abacus { /** * Creates a new instance of the Abacus calculator. */ - public Abacus(){ + public Abacus() { pluginManager = new PluginManager(); numberReducer = new NumberReducer(this); configuration = new ConfigurationObject(CONFIG_FILE); @@ -78,8 +78,19 @@ public class Abacus { pluginManager.load(); } + public static void main(String[] args) { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (ClassNotFoundException | InstantiationException | UnsupportedLookAndFeelException | IllegalAccessException e) { + e.printStackTrace(); + } + + new Window(new Abacus()).setVisible(true); + } + /** * Gets the current tree builder. + * * @return the main tree builder in this abacus instance. */ public TreeBuilder getTreeBuilder() { @@ -88,6 +99,7 @@ public class Abacus { /** * Gets the current plugin manager, + * * @return the plugin manager in this abacus instance. */ public PluginManager getPluginManager() { @@ -97,6 +109,7 @@ public class Abacus { /** * Get the reducer that is responsible for transforming * an expression into a number. + * * @return the number reducer in this abacus instance. */ public NumberReducer getNumberReducer() { @@ -105,6 +118,7 @@ public class Abacus { /** * Gets the configuration object associated with this instance. + * * @return the configuration object. */ public ConfigurationObject getConfiguration() { @@ -114,32 +128,35 @@ public class Abacus { /** * Parses a string into a tree structure using the main * tree builder. + * * @param input the input string to parse * @return the resulting tree, null if the tree builder or the produced tree are null. */ - public TreeNode parseString(String input){ + public TreeNode parseString(String input) { return treeBuilder.fromString(input); } /** * Evaluates the given tree using the main * number reducer. + * * @param tree the tree to reduce, must not be null. * @return the resulting number, or null of the reduction failed. */ - public NumberInterface evaluateTree(TreeNode tree){ + public NumberInterface evaluateTree(TreeNode tree) { return tree.reduce(numberReducer); } /** * Creates a number from a string. + * * @param numberString the string to create the number from. * @return the resulting number. */ - public NumberInterface numberFromString(String numberString){ + public NumberInterface numberFromString(String numberString) { Class toInstantiate = pluginManager.numberFor(configuration.getNumberImplementation()); - if(toInstantiate == null) toInstantiate = DEFAULT_NUMBER; + if (toInstantiate == null) toInstantiate = DEFAULT_NUMBER; try { return toInstantiate.getConstructor(String.class).newInstance(numberString); @@ -148,14 +165,4 @@ public class Abacus { } return null; } - - public static void main(String[] args){ - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (ClassNotFoundException | InstantiationException | UnsupportedLookAndFeelException | IllegalAccessException e) { - e.printStackTrace(); - } - - new Window(new Abacus()).setVisible(true); - } } diff --git a/src/main/java/org/nwapw/abacus/config/ConfigurationObject.java b/src/main/java/org/nwapw/abacus/config/ConfigurationObject.java index 1f0c066..a181038 100644 --- a/src/main/java/org/nwapw/abacus/config/ConfigurationObject.java +++ b/src/main/java/org/nwapw/abacus/config/ConfigurationObject.java @@ -23,56 +23,12 @@ public class ConfigurationObject { */ private Configuration configuration; - /** - * Sets up the ConfigurationObject. - * different constructors do different things, - * but they all lead here. - * @param configuration the configuration to set up with. - */ - private void setup(Configuration configuration){ - this.configuration = configuration; - } - - /** - * Creates a default configuration. - * @return the newly created default configuration. - */ - private Configuration getDefaultConfig(){ - configuration = new Configuration(); - configuration.numberType = "naive"; - return configuration; - } - - /** - * Returns the implementation the user has requested to - * represent their numbers. - * @return the implementation name. - */ - public String getNumberImplementation() { - return configuration.numberType; - } - - /** - * Saves the ConfigurationObject to the given file. - * @param toFile the file to save ot. - * @return true if the save succeed, false if otherwise. - */ - public boolean save(File toFile){ - if(toFile.getParentFile() != null) toFile.getParentFile().mkdirs(); - try { - TOML_WRITER.write(configuration, toFile); - return true; - } catch (IOException e) { - e.printStackTrace(); - } - return false; - } - /** * Creates a new configuration object with the given config. + * * @param config the config to use. */ - public ConfigurationObject(Configuration config){ + public ConfigurationObject(Configuration config) { setup(config); } @@ -80,11 +36,12 @@ public class ConfigurationObject { * Create a configuration object by attempting to * load a config from the given path, using the * default configuration otherwise. + * * @param path the path to attempt to load. */ - public ConfigurationObject(File path){ + public ConfigurationObject(File path) { Configuration config; - if(!path.exists()) { + if (!path.exists()) { config = getDefaultConfig(); } else { Toml parse = new Toml(); @@ -98,8 +55,57 @@ public class ConfigurationObject { * Creates a new configuration object with the * default configuration. */ - public ConfigurationObject(){ + public ConfigurationObject() { setup(getDefaultConfig()); } + /** + * Sets up the ConfigurationObject. + * different constructors do different things, + * but they all lead here. + * + * @param configuration the configuration to set up with. + */ + private void setup(Configuration configuration) { + this.configuration = configuration; + } + + /** + * Creates a default configuration. + * + * @return the newly created default configuration. + */ + private Configuration getDefaultConfig() { + configuration = new Configuration(); + configuration.numberType = "naive"; + return configuration; + } + + /** + * Returns the implementation the user has requested to + * represent their numbers. + * + * @return the implementation name. + */ + public String getNumberImplementation() { + return configuration.numberType; + } + + /** + * Saves the ConfigurationObject to the given file. + * + * @param toFile the file to save ot. + * @return true if the save succeed, false if otherwise. + */ + public boolean save(File toFile) { + if (toFile.getParentFile() != null) toFile.getParentFile().mkdirs(); + try { + TOML_WRITER.write(configuration, toFile); + return true; + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + } diff --git a/src/main/java/org/nwapw/abacus/function/Function.java b/src/main/java/org/nwapw/abacus/function/Function.java index 4300279..62a0e7d 100755 --- a/src/main/java/org/nwapw/abacus/function/Function.java +++ b/src/main/java/org/nwapw/abacus/function/Function.java @@ -23,6 +23,7 @@ public abstract class Function { /** * Checks whether the given params will work for the given function. + * * @param params the given params * @return true if the params can be used with this function. */ @@ -31,6 +32,7 @@ public abstract class Function { /** * Internal apply implementation, which already receives appropriately promoted * parameters that have bee run through matchesParams + * * @param params the promoted parameters. * @return the return value of the function. */ @@ -38,11 +40,12 @@ public abstract class Function { /** * Function to check, promote arguments and run the function. + * * @param params the raw input parameters. * @return the return value of the function, or null if an error occurred. */ - public NumberInterface apply(NumberInterface...params) { - if(!matchesParams(params)) return null; + public NumberInterface apply(NumberInterface... params) { + if (!matchesParams(params)) return null; return applyInternal(params); } diff --git a/src/main/java/org/nwapw/abacus/function/Operator.java b/src/main/java/org/nwapw/abacus/function/Operator.java index 5b1d2b2..eceb179 100644 --- a/src/main/java/org/nwapw/abacus/function/Operator.java +++ b/src/main/java/org/nwapw/abacus/function/Operator.java @@ -24,11 +24,12 @@ public class Operator { /** * Creates a new operator with the given parameters. + * * @param associativity the associativity of the operator. - * @param precedence the precedence of the operator. - * @param function the function that the operator calls. + * @param precedence the precedence of the operator. + * @param function the function that the operator calls. */ - public Operator(OperatorAssociativity associativity, OperatorType operatorType, int precedence, Function function){ + public Operator(OperatorAssociativity associativity, OperatorType operatorType, int precedence, Function function) { this.associativity = associativity; this.type = operatorType; this.precedence = precedence; @@ -37,6 +38,7 @@ public class Operator { /** * Gets the operator's associativity. + * * @return the associativity. */ public OperatorAssociativity getAssociativity() { @@ -45,6 +47,7 @@ public class Operator { /** * Gets the operator's type. + * * @return the type. */ public OperatorType getType() { @@ -53,6 +56,7 @@ public class Operator { /** * Gets the operator's precedence. + * * @return the precedence. */ public int getPrecedence() { @@ -61,6 +65,7 @@ public class Operator { /** * Gets the operator's function. + * * @return the function. */ public Function getFunction() { diff --git a/src/main/java/org/nwapw/abacus/lexing/Lexer.java b/src/main/java/org/nwapw/abacus/lexing/Lexer.java index 82f5898..1b7553b 100644 --- a/src/main/java/org/nwapw/abacus/lexing/Lexer.java +++ b/src/main/java/org/nwapw/abacus/lexing/Lexer.java @@ -10,15 +10,111 @@ import java.util.*; /** * A lexer that can generate tokens of a given type given a list of regular expressions * to operate on. + * * @param the type used to identify which match belongs to which pattern. */ public class Lexer { + /** + * The registered patterns. + */ + private Map, Pattern> patterns; + + /** + * Creates a new lexer with no registered patterns. + */ + public Lexer() { + patterns = new HashMap<>(); + } + + /** + * Registers a single pattern. + * + * @param pattern the pattern regex + * @param id the ID by which to identify the pattern. + */ + public void register(String pattern, T id) { + Pattern compiledPattern = new Pattern<>(pattern, id); + if (compiledPattern.getHead() != null) patterns.put(new PatternEntry<>(pattern, id), compiledPattern); + } + + /** + * Unregisters a pattern. + * + * @param pattern the pattern to unregister + * @param id the ID by which to identify the pattern. + */ + public void unregister(String pattern, T id) { + patterns.remove(new PatternEntry<>(pattern, id)); + } + + /** + * Reads one token from the given string. + * + * @param from the string to read from + * @param startAt the index to start at + * @param compare the comparator used to sort tokens by their ID. + * @return the best match. + */ + public Match lexOne(String from, int startAt, Comparator compare) { + ArrayList> matches = new ArrayList<>(); + HashSet> currentSet = new HashSet<>(); + HashSet> futureSet = new HashSet<>(); + int index = startAt; + for (Pattern pattern : patterns.values()) { + pattern.getHead().addInto(currentSet); + } + while (!currentSet.isEmpty()) { + for (PatternNode node : currentSet) { + if (index < from.length() && node.matches(from.charAt(index))) { + node.addOutputsInto(futureSet); + } else if (node instanceof EndNode) { + matches.add(new Match<>(from.substring(startAt, index), ((EndNode) node).getPatternId())); + } + } + + HashSet> tmp = currentSet; + currentSet = futureSet; + futureSet = tmp; + futureSet.clear(); + + index++; + } + matches.sort((a, b) -> compare.compare(a.getType(), b.getType())); + if (compare != null) { + matches.sort(Comparator.comparingInt(a -> a.getContent().length())); + } + return matches.isEmpty() ? null : matches.get(matches.size() - 1); + } + + /** + * Reads all tokens from a string. + * + * @param from the string to start from. + * @param startAt the index to start at. + * @param compare the comparator used to sort matches by their IDs. + * @return the resulting list of matches, in order, or null on error. + */ + public List> lexAll(String from, int startAt, Comparator compare) { + int index = startAt; + ArrayList> matches = new ArrayList<>(); + Match lastMatch = null; + while (index < from.length() && (lastMatch = lexOne(from, index, compare)) != null) { + int length = lastMatch.getContent().length(); + if (length == 0) return null; + matches.add(lastMatch); + index += length; + } + if (lastMatch == null) return null; + return matches; + } + /** * An entry that represents a pattern that has been registered with the lexer. + * * @param the type used to identify the pattern. */ - private static class PatternEntry{ + private static class PatternEntry { /** * The name of the entry. */ @@ -30,10 +126,11 @@ public class Lexer { /** * Creates a new pattern entry with the given name and id. + * * @param name the name of the pattern entry. - * @param id the id of the pattern entry. + * @param id the id of the pattern entry. */ - public PatternEntry(String name, T id){ + public PatternEntry(String name, T id) { this.name = name; this.id = id; } @@ -51,94 +148,4 @@ public class Lexer { } } - /** - * The registered patterns. - */ - private Map, Pattern> patterns; - - /** - * Creates a new lexer with no registered patterns. - */ - public Lexer(){ - patterns = new HashMap<>(); - } - - /** - * Registers a single pattern. - * @param pattern the pattern regex - * @param id the ID by which to identify the pattern. - */ - public void register(String pattern, T id){ - Pattern compiledPattern = new Pattern<>(pattern, id); - if(compiledPattern.getHead() != null) patterns.put(new PatternEntry<>(pattern, id), compiledPattern); - } - - /** - * Unregisters a pattern. - * @param pattern the pattern to unregister - * @param id the ID by which to identify the pattern. - */ - public void unregister(String pattern, T id){ - patterns.remove(new PatternEntry<>(pattern, id)); - } - - /** - * Reads one token from the given string. - * @param from the string to read from - * @param startAt the index to start at - * @param compare the comparator used to sort tokens by their ID. - * @return the best match. - */ - public Match lexOne(String from, int startAt, Comparator compare){ - ArrayList> matches = new ArrayList<>(); - HashSet> currentSet = new HashSet<>(); - HashSet> futureSet = new HashSet<>(); - int index = startAt; - for(Pattern pattern : patterns.values()){ - pattern.getHead().addInto(currentSet); - } - while(!currentSet.isEmpty()){ - for(PatternNode node : currentSet){ - if(index < from.length() && node.matches(from.charAt(index))) { - node.addOutputsInto(futureSet); - } else if(node instanceof EndNode){ - matches.add(new Match<>(from.substring(startAt, index), ((EndNode) node).getPatternId())); - } - } - - HashSet> tmp = currentSet; - currentSet = futureSet; - futureSet = tmp; - futureSet.clear(); - - index++; - } - matches.sort((a, b) -> compare.compare(a.getType(), b.getType())); - if(compare != null) { - matches.sort(Comparator.comparingInt(a -> a.getContent().length())); - } - return matches.isEmpty() ? null : matches.get(matches.size() - 1); - } - - /** - * Reads all tokens from a string. - * @param from the string to start from. - * @param startAt the index to start at. - * @param compare the comparator used to sort matches by their IDs. - * @return the resulting list of matches, in order, or null on error. - */ - public List> lexAll(String from, int startAt, Comparator compare){ - int index = startAt; - ArrayList> matches = new ArrayList<>(); - Match lastMatch = null; - while(index < from.length() && (lastMatch = lexOne(from, index, compare)) != null){ - int length = lastMatch.getContent().length(); - if(length == 0) return null; - matches.add(lastMatch); - index += length; - } - if(lastMatch == null) return null; - return matches; - } - } diff --git a/src/main/java/org/nwapw/abacus/lexing/pattern/AnyNode.java b/src/main/java/org/nwapw/abacus/lexing/pattern/AnyNode.java index 5b75e51..3db1bbb 100644 --- a/src/main/java/org/nwapw/abacus/lexing/pattern/AnyNode.java +++ b/src/main/java/org/nwapw/abacus/lexing/pattern/AnyNode.java @@ -2,6 +2,7 @@ package org.nwapw.abacus.lexing.pattern; /** * A pattern node that matches any character. + * * @param the type that's used to tell which pattern this node belongs to. */ public class AnyNode extends PatternNode { diff --git a/src/main/java/org/nwapw/abacus/lexing/pattern/EndNode.java b/src/main/java/org/nwapw/abacus/lexing/pattern/EndNode.java index 81be4cd..7bb6301 100644 --- a/src/main/java/org/nwapw/abacus/lexing/pattern/EndNode.java +++ b/src/main/java/org/nwapw/abacus/lexing/pattern/EndNode.java @@ -2,6 +2,7 @@ package org.nwapw.abacus.lexing.pattern; /** * A node that represents a successful match. + * * @param the type that's used to tell which pattern this node belongs to. */ public class EndNode extends PatternNode { @@ -13,17 +14,19 @@ public class EndNode extends PatternNode { /** * Creates a new end node with the given ID. + * * @param patternId the pattern ID. */ - public EndNode(T patternId){ + public EndNode(T patternId) { this.patternId = patternId; } /** * Gets the pattern ID. + * * @return the pattern ID. */ - public T getPatternId(){ + public T getPatternId() { return patternId; } diff --git a/src/main/java/org/nwapw/abacus/lexing/pattern/LinkNode.java b/src/main/java/org/nwapw/abacus/lexing/pattern/LinkNode.java index 63b5335..8ba45bd 100644 --- a/src/main/java/org/nwapw/abacus/lexing/pattern/LinkNode.java +++ b/src/main/java/org/nwapw/abacus/lexing/pattern/LinkNode.java @@ -1,17 +1,17 @@ package org.nwapw.abacus.lexing.pattern; -import java.util.ArrayList; import java.util.Collection; /** * A node that is used as structural glue in pattern compilation. + * * @param the type that's used to tell which pattern this node belongs to. */ public class LinkNode extends PatternNode { @Override public void addInto(Collection> into) { - if(!into.contains(this)) { + if (!into.contains(this)) { into.add(this); addOutputsInto(into); } diff --git a/src/main/java/org/nwapw/abacus/lexing/pattern/Match.java b/src/main/java/org/nwapw/abacus/lexing/pattern/Match.java index 49b7ad8..90edc82 100644 --- a/src/main/java/org/nwapw/abacus/lexing/pattern/Match.java +++ b/src/main/java/org/nwapw/abacus/lexing/pattern/Match.java @@ -2,6 +2,7 @@ package org.nwapw.abacus.lexing.pattern; /** * A match that has been generated by the lexer. + * * @param the type used to represent the ID of the pattern this match belongs to. */ public class Match { @@ -17,16 +18,18 @@ public class Match { /** * Creates a new match with the given parameters. + * * @param content the content of this match. - * @param type the type of the match. + * @param type the type of the match. */ - public Match(String content, T type){ + public Match(String content, T type) { this.content = content; this.type = type; } /** * Gets the content of this match. + * * @return the content. */ public String getContent() { @@ -35,6 +38,7 @@ public class Match { /** * Gets the pattern type of the node. + * * @return the ID of the pattern that this match matched. */ public T getType() { diff --git a/src/main/java/org/nwapw/abacus/lexing/pattern/Pattern.java b/src/main/java/org/nwapw/abacus/lexing/pattern/Pattern.java index 29c3513..06d0d09 100644 --- a/src/main/java/org/nwapw/abacus/lexing/pattern/Pattern.java +++ b/src/main/java/org/nwapw/abacus/lexing/pattern/Pattern.java @@ -8,6 +8,7 @@ import java.util.function.Function; /** * A pattern that can be compiled from a string and used in lexing. + * * @param the type that is used to identify and sort this pattern. */ public class Pattern { @@ -40,13 +41,53 @@ public class Pattern { put('?', Pattern.this::transformQuestion); }}; + /** + * Creates / compiles a new pattern with the given id from the given string. + * + * @param from the string to compile a pattern from. + * @param id the ID to use. + */ + public Pattern(String from, T id) { + this.id = id; + index = 0; + source = from; + + PatternChain chain = parseSegment(false); + if (chain == null) { + head = null; + } else { + chain.append(new EndNode<>(id)); + head = chain.head; + } + } + + /** + * Removes all characters that are considered "special" from + * the given string. + * + * @param from the string to sanitize. + * @return the resulting string. + */ + public static String sanitize(String from) { + Pattern pattern = new Pattern<>("", 0); + from = from.replace(".", "\\."); + from = from.replace("|", "\\|"); + from = from.replace("(", "\\("); + from = from.replace(")", "\\)"); + for (Character key : pattern.operations.keySet()) { + from = from.replace("" + key, "\\" + key); + } + return from; + } + /** * A regex operator function that turns the chain * into a one-or-more chain. + * * @param chain the chain to transform. * @return the modified chain. */ - private PatternChain transformPlus(PatternChain chain){ + private PatternChain transformPlus(PatternChain chain) { chain.tail.outputStates.add(chain.head); return chain; } @@ -54,10 +95,11 @@ public class Pattern { /** * A regex operator function that turns the chain * into a zero-or-more chain. + * * @param chain the chain to transform. * @return the modified chain. */ - private PatternChain transformStar(PatternChain chain){ + private PatternChain transformStar(PatternChain chain) { LinkNode newTail = new LinkNode<>(); LinkNode newHead = new LinkNode<>(); newHead.outputStates.add(chain.head); @@ -72,10 +114,11 @@ public class Pattern { /** * A regex operator function that turns the chain * into a zero-or-one chain. + * * @param chain the chain to transform. * @return the modified chain. */ - private PatternChain transformQuestion(PatternChain chain){ + private PatternChain transformQuestion(PatternChain chain) { LinkNode newTail = new LinkNode<>(); LinkNode newHead = new LinkNode<>(); newHead.outputStates.add(chain.head); @@ -88,14 +131,15 @@ public class Pattern { /** * Combines a collection of chains into one OR chain. + * * @param collection the collection of chains to combine. * @return the resulting OR chain. */ - private PatternChain combineChains(Collection> collection){ + private PatternChain combineChains(Collection> collection) { LinkNode head = new LinkNode<>(); LinkNode tail = new LinkNode<>(); PatternChain newChain = new PatternChain<>(head, tail); - for(PatternChain chain : collection){ + for (PatternChain chain : collection) { head.outputStates.add(chain.head); chain.tail.outputStates.add(tail); } @@ -104,105 +148,108 @@ public class Pattern { /** * Parses a single value from the input into a chain. + * * @return the resulting chain, or null on error. */ - private PatternChain parseValue(){ - if(index >= source.length()) return null; - if(source.charAt(index) == '\\'){ - if(++index >= source.length()) return null; + private PatternChain parseValue() { + if (index >= source.length()) return null; + if (source.charAt(index) == '\\') { + if (++index >= source.length()) return null; } return new PatternChain<>(new ValueNode<>(source.charAt(index++))); } /** * Parses a [] range from the input into a chain. + * * @return the resulting chain, or null on error. */ - private PatternChain parseOr(){ + private PatternChain parseOr() { Stack> orStack = new Stack<>(); index++; - while(index < source.length() && source.charAt(index) != ']'){ - if(source.charAt(index) == '-'){ + while (index < source.length() && source.charAt(index) != ']') { + if (source.charAt(index) == '-') { index++; - if(orStack.empty() || orStack.peek().tail.range() == '\0') return null; + if (orStack.empty() || orStack.peek().tail.range() == '\0') return null; PatternChain bottomRange = orStack.pop(); PatternChain topRange = parseValue(); - if(topRange == null || topRange.tail.range() == '\0') return null; + if (topRange == null || topRange.tail.range() == '\0') return null; orStack.push(new PatternChain<>(new RangeNode<>(bottomRange.tail.range(), topRange.tail.range()))); } else { PatternChain newChain = parseValue(); - if(newChain == null) return null; + if (newChain == null) return null; orStack.push(newChain); } } - if(index++ >= source.length()) return null; + if (index++ >= source.length()) return null; return (orStack.size() == 1) ? orStack.pop() : combineChains(orStack); } /** * Parses a repeatable segment from the input into a chain + * * @param isSubsegment whether the segment is a sub-expression "()", and therefore * whether to expect a closing brace. * @return the resulting chain, or null on error. */ - private PatternChain parseSegment(boolean isSubsegment){ - if(index >= source.length() || ((source.charAt(index) != '(') && isSubsegment)) return null; - if(isSubsegment) index++; + private PatternChain parseSegment(boolean isSubsegment) { + if (index >= source.length() || ((source.charAt(index) != '(') && isSubsegment)) return null; + if (isSubsegment) index++; Stack> orChain = new Stack<>(); PatternChain fullChain = new PatternChain<>(); PatternChain currentChain = null; - while (index < source.length() && source.charAt(index) != ')'){ + while (index < source.length() && source.charAt(index) != ')') { char currentChar = source.charAt(index); - if(operations.containsKey(currentChar)){ - if(currentChain == null) return null; + if (operations.containsKey(currentChar)) { + if (currentChain == null) return null; currentChain = operations.get(currentChar).apply(currentChain); fullChain.append(currentChain); currentChain = null; index++; - } else if(currentChar == '|'){ - if(currentChain == null) return null; + } else if (currentChar == '|') { + if (currentChain == null) return null; fullChain.append(currentChain); orChain.push(fullChain); currentChain = null; fullChain = new PatternChain<>(); - if(++index >= source.length()) return null; - } else if(currentChar == '('){ - if(currentChain != null) { + if (++index >= source.length()) return null; + } else if (currentChar == '(') { + if (currentChain != null) { fullChain.append(currentChain); } currentChain = parseSegment(true); - if(currentChain == null) return null; - } else if(currentChar == '['){ - if(currentChain != null){ + if (currentChain == null) return null; + } else if (currentChar == '[') { + if (currentChain != null) { fullChain.append(currentChain); } currentChain = parseOr(); - if(currentChain == null) return null; - } else if(currentChar == '.'){ - if(currentChain != null){ + if (currentChain == null) return null; + } else if (currentChar == '.') { + if (currentChain != null) { fullChain.append(currentChain); } currentChain = new PatternChain<>(new AnyNode<>()); index++; } else { - if(currentChain != null){ + if (currentChain != null) { fullChain.append(currentChain); } currentChain = parseValue(); - if(currentChain == null) return null; + if (currentChain == null) return null; } } - if(!(!isSubsegment || (index < source.length() && source.charAt(index) == ')'))) return null; - if(isSubsegment) index++; + if (!(!isSubsegment || (index < source.length() && source.charAt(index) == ')'))) return null; + if (isSubsegment) index++; - if(currentChain != null) fullChain.append(currentChain); - if(!orChain.empty()){ + if (currentChain != null) fullChain.append(currentChain); + if (!orChain.empty()) { orChain.push(fullChain); fullChain = combineChains(orChain); } @@ -210,48 +257,12 @@ public class Pattern { return fullChain; } - /** - * Creates / compiles a new pattern with the given id from the given string. - * @param from the string to compile a pattern from. - * @param id the ID to use. - */ - public Pattern(String from, T id){ - this.id = id; - index = 0; - source = from; - - PatternChain chain = parseSegment(false); - if(chain == null) { - head = null; - } else { - chain.append(new EndNode<>(id)); - head = chain.head; - } - } - /** * Gets the head PatternNode, for use in matching + * * @return the pattern node. */ public PatternNode getHead() { return head; } - - /** - * Removes all characters that are considered "special" from - * the given string. - * @param from the string to sanitize. - * @return the resulting string. - */ - public static String sanitize(String from){ - Pattern pattern = new Pattern<>("", 0); - from = from.replace(".", "\\."); - from = from.replace("|", "\\|"); - from = from.replace("(", "\\("); - from = from.replace(")", "\\)"); - for(Character key : pattern.operations.keySet()){ - from = from.replace("" + key, "\\" + key); - } - return from; - } } diff --git a/src/main/java/org/nwapw/abacus/lexing/pattern/PatternChain.java b/src/main/java/org/nwapw/abacus/lexing/pattern/PatternChain.java index a6edb21..1ce9661 100644 --- a/src/main/java/org/nwapw/abacus/lexing/pattern/PatternChain.java +++ b/src/main/java/org/nwapw/abacus/lexing/pattern/PatternChain.java @@ -3,6 +3,7 @@ package org.nwapw.abacus.lexing.pattern; /** * A chain of nodes that can be treated as a single unit. * Used during pattern compilation. + * * @param the type used to identify which pattern has been matched. */ public class PatternChain { @@ -18,26 +19,28 @@ public class PatternChain { /** * Creates a new chain with the given start and end. + * * @param head the start of the chain. * @param tail the end of the chain. */ - public PatternChain(PatternNode head, PatternNode tail){ + public PatternChain(PatternNode head, PatternNode tail) { this.head = head; this.tail = tail; } /** * Creates a chain that starts and ends with the same node. + * * @param node the node to use. */ - public PatternChain(PatternNode node){ + public PatternChain(PatternNode node) { this(node, node); } /** * Creates an empty chain. */ - public PatternChain(){ + public PatternChain() { this(null); } @@ -45,10 +48,11 @@ public class PatternChain { * Appends the other chain to this one. This modifies * the nodes, as well. * If this chain is empty, it is set to the other. + * * @param other the other chain to append. */ - public void append(PatternChain other){ - if(other.head == null || tail == null) { + public void append(PatternChain other) { + if (other.head == null || tail == null) { this.head = other.head; this.tail = other.tail; } else { @@ -61,10 +65,11 @@ public class PatternChain { * Appends a single node to this chain. This modifies * the nodes, as well. * If this chain is empty, it is set to the node. + * * @param node the node to append to this chain. */ - public void append(PatternNode node){ - if(tail == null){ + public void append(PatternNode node) { + if (tail == null) { head = tail = node; } else { tail.outputStates.add(node); diff --git a/src/main/java/org/nwapw/abacus/lexing/pattern/PatternNode.java b/src/main/java/org/nwapw/abacus/lexing/pattern/PatternNode.java index 4108ccc..005f49f 100644 --- a/src/main/java/org/nwapw/abacus/lexing/pattern/PatternNode.java +++ b/src/main/java/org/nwapw/abacus/lexing/pattern/PatternNode.java @@ -1,6 +1,5 @@ package org.nwapw.abacus.lexing.pattern; -import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -9,6 +8,7 @@ import java.util.Set; * A base class for a pattern node. Provides all functions * necessary for matching, and is constructed by a Pattern instance * from a string. + * * @param the type that's used to tell which pattern this node belongs to. */ public class PatternNode { @@ -22,42 +22,46 @@ public class PatternNode { /** * Creates a new pattern node. */ - public PatternNode(){ + public PatternNode() { outputStates = new HashSet<>(); } /** * Determines whether the current input character can * be matched by this node. + * * @param other the character being matched. * @return true if the character can be matched, false otherwise. */ - public boolean matches(char other){ + public boolean matches(char other) { return false; } /** * If this node can be used as part of a range, returns that value. + * * @return a NULL terminator if this character cannot be converted * into a range bound, or the appropriate range bound if it can. */ - public char range(){ + public char range() { return '\0'; } /** * Adds this node in a collection of other nodes. + * * @param into the collection to add into. */ - public void addInto(Collection> into){ + public void addInto(Collection> into) { into.add(this); } /** * Adds the node's children into a collection of other nodes. + * * @param into the collection to add into. */ - public void addOutputsInto(Collection> into){ + public void addOutputsInto(Collection> into) { outputStates.forEach(e -> e.addInto(into)); } diff --git a/src/main/java/org/nwapw/abacus/lexing/pattern/RangeNode.java b/src/main/java/org/nwapw/abacus/lexing/pattern/RangeNode.java index 1eae44c..2956022 100644 --- a/src/main/java/org/nwapw/abacus/lexing/pattern/RangeNode.java +++ b/src/main/java/org/nwapw/abacus/lexing/pattern/RangeNode.java @@ -2,6 +2,7 @@ package org.nwapw.abacus.lexing.pattern; /** * A node that matches a range of characters. + * * @param the type that's used to tell which pattern this node belongs to. */ public class RangeNode extends PatternNode { @@ -17,10 +18,11 @@ public class RangeNode extends PatternNode { /** * Creates a new range node from the given range. + * * @param from the bottom bound of the range. - * @param to the top bound of hte range. + * @param to the top bound of hte range. */ - public RangeNode(char from, char to){ + public RangeNode(char from, char to) { this.from = from; this.to = to; } diff --git a/src/main/java/org/nwapw/abacus/lexing/pattern/ValueNode.java b/src/main/java/org/nwapw/abacus/lexing/pattern/ValueNode.java index b3268ac..4630d91 100644 --- a/src/main/java/org/nwapw/abacus/lexing/pattern/ValueNode.java +++ b/src/main/java/org/nwapw/abacus/lexing/pattern/ValueNode.java @@ -2,6 +2,7 @@ package org.nwapw.abacus.lexing.pattern; /** * A node that matches a single value. + * * @param the type that's used to tell which pattern this node belongs to. */ public class ValueNode extends PatternNode { @@ -13,9 +14,10 @@ public class ValueNode extends PatternNode { /** * Creates a new node that matches the given character. + * * @param value */ - public ValueNode(char value){ + public ValueNode(char value) { this.value = value; } diff --git a/src/main/java/org/nwapw/abacus/number/NaiveNumber.java b/src/main/java/org/nwapw/abacus/number/NaiveNumber.java index 96e42dd..4b502b3 100755 --- a/src/main/java/org/nwapw/abacus/number/NaiveNumber.java +++ b/src/main/java/org/nwapw/abacus/number/NaiveNumber.java @@ -5,26 +5,6 @@ package org.nwapw.abacus.number; */ public class NaiveNumber implements NumberInterface { - /** - * The value of this number. - */ - private double value; - - /** - * Creates a new NaiveNumber with the given string. - * @param value the value, which will be parsed as a double. - */ - public NaiveNumber(String value) { - this(Double.parseDouble(value)); - } - /** - * Creates a new NaiveNumber with the given value. - * @param value the value to use. - */ - public NaiveNumber(double value) { - this.value = value; - } - /** * The number zero. */ @@ -33,6 +13,27 @@ public class NaiveNumber implements NumberInterface { * The number one. */ public static final NaiveNumber ONE = new NaiveNumber(1); + /** + * The value of this number. + */ + private double value; + + /** + * Creates a new NaiveNumber with the given string. + * + * @param value the value, which will be parsed as a double. + */ + public NaiveNumber(String value) { + this(Double.parseDouble(value)); + } + /** + * Creates a new NaiveNumber with the given value. + * + * @param value the value to use. + */ + public NaiveNumber(double value) { + this.value = value; + } @Override public int getMaxPrecision() { @@ -41,22 +42,22 @@ public class NaiveNumber implements NumberInterface { @Override public NumberInterface multiply(NumberInterface multiplier) { - return new NaiveNumber(value * ((NaiveNumber)multiplier).value); + return new NaiveNumber(value * ((NaiveNumber) multiplier).value); } @Override public NumberInterface divide(NumberInterface divisor) { - return new NaiveNumber(value / ((NaiveNumber)divisor).value); + return new NaiveNumber(value / ((NaiveNumber) divisor).value); } @Override public NumberInterface add(NumberInterface summand) { - return new NaiveNumber(value + ((NaiveNumber)summand).value); + return new NaiveNumber(value + ((NaiveNumber) summand).value); } @Override public NumberInterface subtract(NumberInterface subtrahend) { - return new NaiveNumber(value - ((NaiveNumber)subtrahend).value); + return new NaiveNumber(value - ((NaiveNumber) subtrahend).value); } @Override @@ -66,16 +67,16 @@ public class NaiveNumber implements NumberInterface { @Override public NumberInterface intPow(int exponent) { - if(exponent == 0){ + if (exponent == 0) { return NaiveNumber.ONE; } boolean takeReciprocal = exponent < 0; exponent = Math.abs(exponent); NumberInterface power = this; - for(int currentExponent = 1; currentExponent < exponent; currentExponent++){ + for (int currentExponent = 1; currentExponent < exponent; currentExponent++) { power = power.multiply(this); } - if(takeReciprocal){ + if (takeReciprocal) { power = NaiveNumber.ONE.divide(power); } return power; @@ -94,14 +95,14 @@ public class NaiveNumber implements NumberInterface { @Override public NumberInterface promoteTo(Class toClass) { - if(toClass == this.getClass()) return this; - else if(toClass == PreciseNumber.class){ + if (toClass == this.getClass()) return this; + else if (toClass == PreciseNumber.class) { return new PreciseNumber(Double.toString(value)); } return null; } - public String toString(){ + public String toString() { double shiftBy = Math.pow(10, 10); return Double.toString(Math.round(value * shiftBy) / shiftBy); } diff --git a/src/main/java/org/nwapw/abacus/number/NumberInterface.java b/src/main/java/org/nwapw/abacus/number/NumberInterface.java index 1f87d11..74b4d4c 100755 --- a/src/main/java/org/nwapw/abacus/number/NumberInterface.java +++ b/src/main/java/org/nwapw/abacus/number/NumberInterface.java @@ -7,6 +7,7 @@ public interface NumberInterface { /** * The maximum precision to which this number operates. + * * @return the precision. */ int getMaxPrecision(); @@ -14,27 +15,34 @@ public interface NumberInterface { /** * Multiplies this number by another, returning * a new number instance. + * * @param multiplier the multiplier * @return the result of the multiplication. */ NumberInterface multiply(NumberInterface multiplier); + /** * Divides this number by another, returning * a new number instance. + * * @param divisor the divisor * @return the result of the division. */ NumberInterface divide(NumberInterface divisor); + /** * Adds this number to another, returning * a new number instance. + * * @param summand the summand * @return the result of the summation. */ NumberInterface add(NumberInterface summand); + /** * Subtracts another number from this number, * a new number instance. + * * @param subtrahend the subtrahend. * @return the result of the subtraction. */ @@ -43,12 +51,14 @@ public interface NumberInterface { /** * Returns a new instance of this number with * the sign flipped. + * * @return the new instance. */ NumberInterface negate(); /** * Raises this number to an integer power. + * * @param exponent the exponent to which to take the number. * @return the resulting value. */ @@ -56,6 +66,7 @@ public interface NumberInterface { /** * Compares this number to another. + * * @param number the number to compare to. * @return same as Integer.compare(); */ @@ -63,12 +74,14 @@ public interface NumberInterface { /** * Same as Math.signum(). + * * @return 1 if this number is positive, -1 if this number is negative, 0 if this number is 0. */ int signum(); /** * Promotes this class to another number class. + * * @param toClass the class to promote to. * @return the resulting new instance. */ diff --git a/src/main/java/org/nwapw/abacus/number/PreciseNumber.java b/src/main/java/org/nwapw/abacus/number/PreciseNumber.java index 8b01622..172a1c4 100755 --- a/src/main/java/org/nwapw/abacus/number/PreciseNumber.java +++ b/src/main/java/org/nwapw/abacus/number/PreciseNumber.java @@ -3,7 +3,7 @@ package org.nwapw.abacus.number; import java.math.BigDecimal; import java.math.RoundingMode; -public class PreciseNumber implements NumberInterface{ +public class PreciseNumber implements NumberInterface { /** * The number one. @@ -25,18 +25,20 @@ public class PreciseNumber implements NumberInterface{ /** * Constructs a precise number from the given string. + * * @param string a string representation of the number meeting the same conditions * as the BidDecimal(String) constructor. */ - public PreciseNumber(String string){ + public PreciseNumber(String string) { value = new BigDecimal(string); } /** * Constructs a precise number from the given BigDecimal. + * * @param value a BigDecimal object representing the value of the number. */ - public PreciseNumber(BigDecimal value){ + public PreciseNumber(BigDecimal value) { this.value = value; } @@ -67,16 +69,16 @@ public class PreciseNumber implements NumberInterface{ @Override public NumberInterface intPow(int exponent) { - if(exponent == 0){ + if (exponent == 0) { return PreciseNumber.ONE; } boolean takeReciprocal = exponent < 0; exponent = Math.abs(exponent); NumberInterface power = this; - for(int currentExponent = 1; currentExponent < exponent; currentExponent++){ + for (int currentExponent = 1; currentExponent < exponent; currentExponent++) { power = power.multiply(this); } - if(takeReciprocal){ + if (takeReciprocal) { power = PreciseNumber.ONE.divide(power); } return power; @@ -93,13 +95,13 @@ public class PreciseNumber implements NumberInterface{ } @Override - public NumberInterface negate(){ + public NumberInterface negate() { return new PreciseNumber(value.negate()); } @Override public NumberInterface promoteTo(Class toClass) { - if(toClass == this.getClass()){ + if (toClass == this.getClass()) { return this; } return null; diff --git a/src/main/java/org/nwapw/abacus/parsing/LexerTokenizer.java b/src/main/java/org/nwapw/abacus/parsing/LexerTokenizer.java index 104541e..790882a 100644 --- a/src/main/java/org/nwapw/abacus/parsing/LexerTokenizer.java +++ b/src/main/java/org/nwapw/abacus/parsing/LexerTokenizer.java @@ -29,7 +29,7 @@ public class LexerTokenizer implements Tokenizer>, PluginListen /** * Creates a new lexer tokenizer. */ - public LexerTokenizer(){ + public LexerTokenizer() { lexer = new Lexer() {{ register(" ", TokenType.WHITESPACE); register(",", TokenType.COMMA); @@ -46,20 +46,20 @@ public class LexerTokenizer implements Tokenizer>, PluginListen @Override public void onLoad(PluginManager manager) { - for(String operator : manager.getAllOperators()){ - lexer.register(Pattern.sanitize(operator), TokenType.OP); + for (String operator : manager.getAllOperators()) { + lexer.register(Pattern.sanitize(operator), TokenType.OP); } - for(String function : manager.getAllFunctions()){ + for (String function : manager.getAllFunctions()) { lexer.register(Pattern.sanitize(function), TokenType.FUNCTION); } } @Override public void onUnload(PluginManager manager) { - for(String operator : manager.getAllOperators()){ + for (String operator : manager.getAllOperators()) { lexer.unregister(Pattern.sanitize(operator), TokenType.OP); } - for(String function : manager.getAllFunctions()){ + for (String function : manager.getAllFunctions()) { lexer.unregister(Pattern.sanitize(function), TokenType.FUNCTION); } } diff --git a/src/main/java/org/nwapw/abacus/parsing/Parser.java b/src/main/java/org/nwapw/abacus/parsing/Parser.java index d4a72d0..f2c80fc 100644 --- a/src/main/java/org/nwapw/abacus/parsing/Parser.java +++ b/src/main/java/org/nwapw/abacus/parsing/Parser.java @@ -7,12 +7,14 @@ import java.util.List; /** * An itnerface that provides the ability to convert a list of tokens * into a parse tree. + * * @param the type of tokens accepted by this parser. */ public interface Parser { /** * Constructs a tree out of the given tokens. + * * @param tokens the tokens to construct a tree from. * @return the constructed tree, or null on error. */ diff --git a/src/main/java/org/nwapw/abacus/parsing/ShuntingYardParser.java b/src/main/java/org/nwapw/abacus/parsing/ShuntingYardParser.java index 9934006..e807344 100644 --- a/src/main/java/org/nwapw/abacus/parsing/ShuntingYardParser.java +++ b/src/main/java/org/nwapw/abacus/parsing/ShuntingYardParser.java @@ -36,9 +36,10 @@ public class ShuntingYardParser implements Parser>, PluginListe /** * Creates a new Shunting Yard parser with the given Abacus instance. + * * @param abacus the abacus instance. */ - public ShuntingYardParser(Abacus abacus){ + public ShuntingYardParser(Abacus abacus) { this.abacus = abacus; precedenceMap = new HashMap<>(); associativityMap = new HashMap<>(); @@ -47,39 +48,40 @@ public class ShuntingYardParser implements Parser>, PluginListe /** * Rearranges tokens into a postfix list, using Shunting Yard. + * * @param from the tokens to be rearranged. * @return the resulting list of rearranged tokens. */ - public List> intoPostfix(List> from){ + public List> intoPostfix(List> from) { ArrayList> output = new ArrayList<>(); Stack> tokenStack = new Stack<>(); - while(!from.isEmpty()){ + while (!from.isEmpty()) { Match match = from.remove(0); TokenType matchType = match.getType(); - if(matchType == TokenType.NUM) { + if (matchType == TokenType.NUM) { output.add(match); - } else if(matchType == TokenType.FUNCTION) { - output.add(new Match<>("" , TokenType.INTERNAL_FUNCTION_END)); + } else if (matchType == TokenType.FUNCTION) { + output.add(new Match<>("", TokenType.INTERNAL_FUNCTION_END)); tokenStack.push(match); - } else if(matchType == TokenType.OP){ + } else if (matchType == TokenType.OP) { String tokenString = match.getContent(); OperatorType type = typeMap.get(tokenString); int precedence = precedenceMap.get(tokenString); OperatorAssociativity associativity = associativityMap.get(tokenString); - if(type == OperatorType.UNARY_POSTFIX){ + if (type == OperatorType.UNARY_POSTFIX) { output.add(match); continue; } - while(!tokenStack.empty()) { + while (!tokenStack.empty()) { Match otherMatch = tokenStack.peek(); TokenType otherMatchType = otherMatch.getType(); - if(!(otherMatchType == TokenType.OP || otherMatchType == TokenType.FUNCTION)) break; + if (!(otherMatchType == TokenType.OP || otherMatchType == TokenType.FUNCTION)) break; - if(otherMatchType == TokenType.OP){ + if (otherMatchType == TokenType.OP) { int otherPrecedence = precedenceMap.get(match.getContent()); - if(otherPrecedence < precedence || + if (otherPrecedence < precedence || (associativity == OperatorAssociativity.RIGHT && otherPrecedence == precedence)) { break; } @@ -87,22 +89,22 @@ public class ShuntingYardParser implements Parser>, PluginListe output.add(tokenStack.pop()); } tokenStack.push(match); - } else if(matchType == TokenType.OPEN_PARENTH){ + } else if (matchType == TokenType.OPEN_PARENTH) { tokenStack.push(match); - } else if(matchType == TokenType.CLOSE_PARENTH || matchType == TokenType.COMMA){ - while(!tokenStack.empty() && tokenStack.peek().getType() != TokenType.OPEN_PARENTH){ + } else if (matchType == TokenType.CLOSE_PARENTH || matchType == TokenType.COMMA) { + while (!tokenStack.empty() && tokenStack.peek().getType() != TokenType.OPEN_PARENTH) { output.add(tokenStack.pop()); } - if(tokenStack.empty()) return null; - if(matchType == TokenType.CLOSE_PARENTH){ + if (tokenStack.empty()) return null; + if (matchType == TokenType.CLOSE_PARENTH) { tokenStack.pop(); } } } - while(!tokenStack.empty()){ + while (!tokenStack.empty()) { Match match = tokenStack.peek(); TokenType matchType = match.getType(); - if(!(matchType == TokenType.OP || matchType == TokenType.FUNCTION)) return null; + if (!(matchType == TokenType.OP || matchType == TokenType.FUNCTION)) return null; output.add(tokenStack.pop()); } return output; @@ -110,37 +112,38 @@ public class ShuntingYardParser implements Parser>, PluginListe /** * Constructs a tree recursively from a list of tokens. + * * @param matches the list of tokens from the source string. * @return the construct tree expression. */ - public TreeNode constructRecursive(List> matches){ - if(matches.size() == 0) return null; + public TreeNode constructRecursive(List> matches) { + if (matches.size() == 0) return null; Match match = matches.remove(0); TokenType matchType = match.getType(); - if(matchType == TokenType.OP){ + if (matchType == TokenType.OP) { String operator = match.getContent(); OperatorType type = typeMap.get(operator); - if(type == OperatorType.BINARY_INFIX){ + if (type == OperatorType.BINARY_INFIX) { TreeNode right = constructRecursive(matches); TreeNode left = constructRecursive(matches); - if(left == null || right == null) return null; + if (left == null || right == null) return null; else return new BinaryInfixNode(operator, left, right); } else { TreeNode applyTo = constructRecursive(matches); - if(applyTo == null) return null; + if (applyTo == null) return null; else return new UnaryPrefixNode(operator, applyTo); } - } else if(matchType == TokenType.NUM){ + } else if (matchType == TokenType.NUM) { return new NumberNode(abacus.numberFromString(match.getContent())); - } else if(matchType == TokenType.FUNCTION){ + } else if (matchType == TokenType.FUNCTION) { String functionName = match.getContent(); FunctionNode node = new FunctionNode(functionName); - while(!matches.isEmpty() && matches.get(0).getType() != TokenType.INTERNAL_FUNCTION_END){ + while (!matches.isEmpty() && matches.get(0).getType() != TokenType.INTERNAL_FUNCTION_END) { TreeNode argument = constructRecursive(matches); - if(argument == null) return null; + if (argument == null) return null; node.prependChild(argument); } - if(matches.isEmpty()) return null; + if (matches.isEmpty()) return null; matches.remove(0); return node; } @@ -156,7 +159,7 @@ public class ShuntingYardParser implements Parser>, PluginListe @Override public void onLoad(PluginManager manager) { - for(String operator : manager.getAllOperators()){ + for (String operator : manager.getAllOperators()) { Operator operatorInstance = manager.operatorFor(operator); precedenceMap.put(operator, operatorInstance.getPrecedence()); associativityMap.put(operator, operatorInstance.getAssociativity()); diff --git a/src/main/java/org/nwapw/abacus/parsing/Tokenizer.java b/src/main/java/org/nwapw/abacus/parsing/Tokenizer.java index 0f1b270..00606d0 100644 --- a/src/main/java/org/nwapw/abacus/parsing/Tokenizer.java +++ b/src/main/java/org/nwapw/abacus/parsing/Tokenizer.java @@ -4,12 +4,14 @@ import java.util.List; /** * Interface that provides the ability to convert a string into a list of tokens. + * * @param the type of the tokens produced. */ public interface Tokenizer { /** * Converts a string into tokens. + * * @param string the string to convert. * @return the list of tokens, or null on error. */ diff --git a/src/main/java/org/nwapw/abacus/parsing/TreeBuilder.java b/src/main/java/org/nwapw/abacus/parsing/TreeBuilder.java index 76eebee..b3d597e 100644 --- a/src/main/java/org/nwapw/abacus/parsing/TreeBuilder.java +++ b/src/main/java/org/nwapw/abacus/parsing/TreeBuilder.java @@ -10,6 +10,7 @@ import java.util.List; * working with any parameters at all, and the generics * in this class are used only to ensure the tokenizer and parser * are of the same type. + * * @param the type of tokens created by the tokenizer and used by the parser. */ public class TreeBuilder { @@ -25,22 +26,24 @@ public class TreeBuilder { /** * Create a new Tree Builder with the given tokenizer and parser + * * @param tokenizer the tokenizer to turn strings into tokens - * @param parser the parser to turn tokens into a tree + * @param parser the parser to turn tokens into a tree */ - public TreeBuilder(Tokenizer tokenizer, Parser parser){ + public TreeBuilder(Tokenizer tokenizer, Parser parser) { this.tokenizer = tokenizer; this.parser = parser; } /** * Parse the given string into a tree. + * * @param input the string to parse into a tree. * @return the resulting tree. */ - public TreeNode fromString(String input){ + public TreeNode fromString(String input) { List tokens = tokenizer.tokenizeString(input); - if(tokens == null) return null; + if (tokens == null) return null; return parser.constructTree(tokens); } diff --git a/src/main/java/org/nwapw/abacus/plugin/ClassFinder.java b/src/main/java/org/nwapw/abacus/plugin/ClassFinder.java index cfd26f6..90437f6 100644 --- a/src/main/java/org/nwapw/abacus/plugin/ClassFinder.java +++ b/src/main/java/org/nwapw/abacus/plugin/ClassFinder.java @@ -20,9 +20,10 @@ public class ClassFinder { /** * Loads all the plugin classes from the given plugin folder. + * * @param filePath the path for the plugin folder. * @return the list of all loaded classes. - * @throws IOException thrown if an error occurred scanning the plugin folder. + * @throws IOException thrown if an error occurred scanning the plugin folder. * @throws ClassNotFoundException thrown if the class listed in the file doesn't get loaded. */ public static List> loadJars(String filePath) throws IOException, ClassNotFoundException { @@ -31,19 +32,20 @@ public class ClassFinder { /** * Loads all the plugin classes from the given plugin folder. + * * @param pluginFolderPath the folder in which to look for plugins. * @return the list of all loaded classes. - * @throws IOException thrown if an error occurred scanning the plugin folder. + * @throws IOException thrown if an error occurred scanning the plugin folder. * @throws ClassNotFoundException thrown if the class listed in the file doesn't get loaded. */ public static List> loadJars(File pluginFolderPath) throws IOException, ClassNotFoundException { ArrayList> toReturn = new ArrayList<>(); - if(!pluginFolderPath.exists()) return toReturn; + if (!pluginFolderPath.exists()) return toReturn; ArrayList files = Files.walk(pluginFolderPath.toPath()) .map(Path::toFile) .filter(f -> f.getName().endsWith(".jar")) .collect(Collectors.toCollection(ArrayList::new)); - for (File file : files){ + for (File file : files) { toReturn.addAll(loadJar(file)); } return toReturn; @@ -51,9 +53,10 @@ public class ClassFinder { /** * Loads the classes from a single path, given by the file. + * * @param jarLocation the location of the jar to load. * @return the list of loaded classes loaded from the jar. - * @throws IOException thrown if there was an error reading the file + * @throws IOException thrown if there was an error reading the file * @throws ClassNotFoundException thrown if the class could not be loaded. */ public static List> loadJar(File jarLocation) throws IOException, ClassNotFoundException { diff --git a/src/main/java/org/nwapw/abacus/plugin/Plugin.java b/src/main/java/org/nwapw/abacus/plugin/Plugin.java index 2bf4a2d..62f6ecf 100644 --- a/src/main/java/org/nwapw/abacus/plugin/Plugin.java +++ b/src/main/java/org/nwapw/abacus/plugin/Plugin.java @@ -39,10 +39,12 @@ public abstract class Plugin { */ private boolean enabled; - private Plugin(){ } + private Plugin() { + } /** * Creates a new plugin with the given PluginManager. + * * @param manager the manager controlling this plugin. */ public Plugin(PluginManager manager) { @@ -55,30 +57,34 @@ public abstract class Plugin { /** * Gets the list of functions provided by this plugin. + * * @return the list of registered functions. */ - public final Set providedFunctions(){ + public final Set providedFunctions() { return functions.keySet(); } /** * Gets the list of functions provided by this plugin. + * * @return the list of registered functions. */ - public final Set providedOperators(){ + public final Set providedOperators() { return operators.keySet(); } /** * Gets the list of all numbers provided by this plugin. + * * @return the list of registered numbers. */ - public final Set providedNumbers(){ + public final Set providedNumbers() { return numbers.keySet(); } /** * Gets a function under the given function name. + * * @param functionName the name of the function to get * @return the function, or null if this plugin doesn't provide it. */ @@ -88,6 +94,7 @@ public abstract class Plugin { /** * Gets an operator under the given operator name. + * * @param operatorName the name of the operator to get. * @return the operator, or null if this plugin doesn't provide it. */ @@ -97,10 +104,11 @@ public abstract class Plugin { /** * Gets the class under the given name. + * * @param numberName the name of the class. * @return the class, or null if the plugin doesn't provide it. */ - public final Class getNumber(String numberName){ + public final Class getNumber(String numberName) { return numbers.get(numberName); } @@ -108,8 +116,8 @@ public abstract class Plugin { * Enables the function, loading the necessary instances * of functions. */ - public final void enable(){ - if(enabled) return; + public final void enable() { + if (enabled) return; onEnable(); enabled = true; } @@ -118,8 +126,8 @@ public abstract class Plugin { * Disables the plugin, clearing loaded data store by default * and calling its disable() method. */ - public final void disable(){ - if(!enabled) return; + public final void disable() { + if (!enabled) return; onDisable(); functions.clear(); operators.clear(); @@ -129,7 +137,8 @@ public abstract class Plugin { /** * To be used in load(). Registers a function abstract class with the * plugin internally, which makes it accessible to the plugin manager. - * @param name the name to register by. + * + * @param name the name to register by. * @param toRegister the function implementation. */ protected final void registerFunction(String name, Function toRegister) { @@ -140,7 +149,8 @@ public abstract class Plugin { * To be used in load(). Registers an operator abstract class * with the plugin internally, which makes it accessible to * the plugin manager. - * @param name the name of the operator. + * + * @param name the name of the operator. * @param operator the operator to register. */ protected final void registerOperator(String name, Operator operator) { @@ -152,10 +162,11 @@ public abstract class Plugin { * with the plugin internally, which makes it possible * for the user to select it as an "implementation" for the * number that they would like to use. - * @param name the name to register it under. + * + * @param name the name to register it under. * @param toRegister the class to register. */ - protected final void registerNumber(String name, Class toRegister){ + protected final void registerNumber(String name, Class toRegister) { numbers.put(name, toRegister); } @@ -163,6 +174,7 @@ public abstract class Plugin { * Searches the PluginManager for the given function name. * This can be used by the plugins internally in order to call functions * they do not provide. + * * @param name the name for which to search * @return the resulting function, or null if none was found for that name. */ @@ -174,6 +186,7 @@ public abstract class Plugin { * Searches the PluginManager for the given operator name. * This can be used by the plugins internally in order to call * operations they do not provide. + * * @param name the name for which to search * @return the resulting operator, or null if none was found for that name. */ diff --git a/src/main/java/org/nwapw/abacus/plugin/PluginListener.java b/src/main/java/org/nwapw/abacus/plugin/PluginListener.java index bfe00ee..c8e78da 100644 --- a/src/main/java/org/nwapw/abacus/plugin/PluginListener.java +++ b/src/main/java/org/nwapw/abacus/plugin/PluginListener.java @@ -7,12 +7,14 @@ public interface PluginListener { /** * Called when the PluginManager loads plugins. + * * @param manager the manager that fired the event. */ public void onLoad(PluginManager manager); /** * Called when the PluginManager unloads all its plugins. + * * @param manager the manager that fired the event. */ public void onUnload(PluginManager manager); diff --git a/src/main/java/org/nwapw/abacus/plugin/PluginManager.java b/src/main/java/org/nwapw/abacus/plugin/PluginManager.java index ee10747..757ac67 100644 --- a/src/main/java/org/nwapw/abacus/plugin/PluginManager.java +++ b/src/main/java/org/nwapw/abacus/plugin/PluginManager.java @@ -56,7 +56,7 @@ public class PluginManager { /** * Creates a new plugin manager. */ - public PluginManager(){ + public PluginManager() { loadedPluginClasses = new HashSet<>(); plugins = new HashSet<>(); cachedFunctions = new HashMap<>(); @@ -73,23 +73,24 @@ public class PluginManager { * list of items of the type using the setFunction and getting the value * of it is available via getFunction. If the value is contained * in the cache, it returns the cached value instead. - * @param plugins the plugin list to search. - * @param cache the cache to use + * + * @param plugins the plugin list to search. + * @param cache the cache to use * @param setFunction the function to retrieve a set of available T's from the plugin * @param getFunction the function to get the T value under the given name - * @param name the name to search for - * @param the type of element being search + * @param name the name to search for + * @param the type of element being search * @return the retrieved element, or null if it was not found. */ private static T searchCached(Collection plugins, Map cache, java.util.function.Function> setFunction, java.util.function.BiFunction getFunction, - String name){ - if(cache.containsKey(name)) return cache.get(name); + String name) { + if (cache.containsKey(name)) return cache.get(name); T loadedValue = null; - for(Plugin plugin : plugins){ - if(setFunction.apply(plugin).contains(name)){ + for (Plugin plugin : plugins) { + if (setFunction.apply(plugin).contains(name)) { loadedValue = getFunction.apply(plugin, name); break; } @@ -98,39 +99,44 @@ public class PluginManager { cache.put(name, loadedValue); return loadedValue; } + /** * Gets a function under the given name. + * * @param name the name of the function * @return the function under the given name. */ - public Function functionFor(String name){ + public Function functionFor(String name) { return searchCached(plugins, cachedFunctions, Plugin::providedFunctions, Plugin::getFunction, name); } /** * Gets an operator under the given name. + * * @param name the name of the operator. * @return the operator under the given name. */ - public Operator operatorFor(String name){ + public Operator operatorFor(String name) { return searchCached(plugins, cachedOperators, Plugin::providedOperators, Plugin::getOperator, name); } /** * Gets a numer implementation under the given name. + * * @param name the name of the implementation. * @return the implementation class */ - public Class numberFor(String name){ + public Class numberFor(String name) { return searchCached(plugins, cachedNumbers, Plugin::providedNumbers, Plugin::getNumber, name); } /** * Adds an instance of Plugin that already has been instantiated. + * * @param plugin the plugin to add. */ - public void addInstantiated(Plugin plugin){ - if(loadedPluginClasses.contains(plugin.getClass())) return; + public void addInstantiated(Plugin plugin) { + if (loadedPluginClasses.contains(plugin.getClass())) return; plugins.add(plugin); loadedPluginClasses.add(plugin.getClass()); } @@ -138,10 +144,11 @@ public class PluginManager { /** * Instantiates a class of plugin, and adds it to this * plugin manager. + * * @param newClass the new class to instantiate. */ - public void addClass(Class newClass){ - if(!Plugin.class.isAssignableFrom(newClass) || newClass == Plugin.class) return; + public void addClass(Class newClass) { + if (!Plugin.class.isAssignableFrom(newClass) || newClass == Plugin.class) return; try { addInstantiated((Plugin) newClass.getConstructor(PluginManager.class).newInstance(this)); } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { @@ -152,9 +159,9 @@ public class PluginManager { /** * Loads all the plugins in the PluginManager. */ - public void load(){ - for(Plugin plugin : plugins) plugin.enable(); - for(Plugin plugin : plugins){ + public void load() { + for (Plugin plugin : plugins) plugin.enable(); + for (Plugin plugin : plugins) { allFunctions.addAll(plugin.providedFunctions()); allOperators.addAll(plugin.providedOperators()); allNumbers.addAll(plugin.providedNumbers()); @@ -165,8 +172,8 @@ public class PluginManager { /** * Unloads all the plugins in the PluginManager. */ - public void unload(){ - for(Plugin plugin : plugins) plugin.disable(); + public void unload() { + for (Plugin plugin : plugins) plugin.disable(); allFunctions.clear(); allOperators.clear(); allNumbers.clear(); @@ -176,13 +183,14 @@ public class PluginManager { /** * Reloads all the plugins in the PluginManager. */ - public void reload(){ + public void reload() { unload(); reload(); } /** * Gets all the functions loaded by the Plugin Manager. + * * @return the set of all functions that were loaded. */ public Set getAllFunctions() { @@ -191,6 +199,7 @@ public class PluginManager { /** * Gets all the operators loaded by the Plugin Manager. + * * @return the set of all operators that were loaded. */ public Set getAllOperators() { @@ -199,6 +208,7 @@ public class PluginManager { /** * Gets all the number implementations loaded by the Plugin Manager + * * @return the set of all implementations that were loaded */ public Set getAllNumbers() { @@ -207,18 +217,20 @@ public class PluginManager { /** * Adds a plugin change listener to this plugin manager. + * * @param listener the listener to add. */ - public void addListener(PluginListener listener){ + public void addListener(PluginListener listener) { listeners.add(listener); } /** * Remove the plugin change listener from this plugin manager. + * * @param listener the listener to remove. */ - public void removeListener(PluginListener listener){ + public void removeListener(PluginListener listener) { listeners.remove(listener); } - + } diff --git a/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java b/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java index d15395f..42b8355 100755 --- a/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java +++ b/src/main/java/org/nwapw/abacus/plugin/StandardPlugin.java @@ -25,7 +25,7 @@ public class StandardPlugin extends Plugin { @Override protected NumberInterface applyInternal(NumberInterface[] params) { NumberInterface sum = params[0]; - for(int i = 1; i < params.length; i++){ + for (int i = 1; i < params.length; i++) { sum = sum.add(params[i]); } return sum; @@ -42,7 +42,7 @@ public class StandardPlugin extends Plugin { return params[0].subtract(params[1]); } }); - public static final Operator OP_MULTIPLY = new Operator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX,1, new Function() { + public static final Operator OP_MULTIPLY = new Operator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1, new Function() { @Override protected boolean matchesParams(NumberInterface[] params) { return params.length >= 1; @@ -51,13 +51,13 @@ public class StandardPlugin extends Plugin { @Override protected NumberInterface applyInternal(NumberInterface[] params) { NumberInterface product = params[0]; - for(int i = 1; i < params.length; i++){ + for (int i = 1; i < params.length; i++) { product = product.multiply(params[i]); } return product; } }); - public static final Operator OP_DIVIDE = new Operator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX,1, new Function() { + public static final Operator OP_DIVIDE = new Operator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1, new Function() { @Override protected boolean matchesParams(NumberInterface[] params) { return params.length >= 1; @@ -66,23 +66,12 @@ public class StandardPlugin extends Plugin { @Override protected NumberInterface applyInternal(NumberInterface[] params) { NumberInterface product = params[0]; - for(int i = 1; i < params.length; i++){ + for (int i = 1; i < params.length; i++) { product = product.multiply(params[i]); } return product; } }); - public static final Operator OP_CARET = new Operator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 2, new Function() { - @Override - protected boolean matchesParams(NumberInterface[] params) { - return params.length == 2; - } - - @Override - protected NumberInterface applyInternal(NumberInterface[] params) { - return FUNCTION_EXP.apply(FUNCTION_LN.apply(params[0]).multiply(params[1])); - } - }); public static final Operator OP_FACTORIAL = new Operator(OperatorAssociativity.RIGHT, OperatorType.UNARY_POSTFIX, 0, new Function() { //private HashMap, ArrayList> storedList = new HashMap, ArrayList>(); @Override @@ -92,13 +81,13 @@ public class StandardPlugin extends Plugin { @Override protected NumberInterface applyInternal(NumberInterface[] params) { - if(params[0].signum() == 0){ + if (params[0].signum() == 0) { return (new NaiveNumber(1)).promoteTo(params[0].getClass()); } NumberInterface factorial = params[0]; NumberInterface multiplier = params[0]; //It is necessary to later prevent calls of factorial on anything but non-negative integers. - while((multiplier = multiplier.subtract(NaiveNumber.ONE.promoteTo(multiplier.getClass()))).signum() == 1){ + while ((multiplier = multiplier.subtract(NaiveNumber.ONE.promoteTo(multiplier.getClass()))).signum() == 1) { factorial = factorial.multiply(multiplier); } return factorial; @@ -131,7 +120,7 @@ public class StandardPlugin extends Plugin { boolean takeReciprocal = params[0].signum() == -1; params[0] = FUNCTION_ABS.apply(params[0]); NumberInterface sum = sumSeries(params[0], StandardPlugin::getExpSeriesTerm, getNTermsExp(getMaxError(params[0]), params[0])); - if(takeReciprocal){ + if (takeReciprocal) { sum = NaiveNumber.ONE.promoteTo(sum.getClass()).divide(sum); } return sum; @@ -147,19 +136,18 @@ public class StandardPlugin extends Plugin { protected NumberInterface applyInternal(NumberInterface[] params) { NumberInterface param = params[0]; int powersOf2 = 0; - while(FUNCTION_ABS.apply(param.subtract(NaiveNumber.ONE.promoteTo(param.getClass()))).compareTo((new NaiveNumber(0.1)).promoteTo(param.getClass())) >= 0){ - if(param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() == 1) { + while (FUNCTION_ABS.apply(param.subtract(NaiveNumber.ONE.promoteTo(param.getClass()))).compareTo((new NaiveNumber(0.1)).promoteTo(param.getClass())) >= 0) { + if (param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() == 1) { param = param.divide(new NaiveNumber(2).promoteTo(param.getClass())); powersOf2++; - if(param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() != 1) { + if (param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() != 1) { break; //No infinite loop for you. } - } - else { + } else { param = param.multiply(new NaiveNumber(2).promoteTo(param.getClass())); powersOf2--; - if(param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() != 1) { + if (param.subtract(NaiveNumber.ONE.promoteTo(param.getClass())).signum() != 1) { break; //No infinite loop for you. } @@ -174,14 +162,14 @@ public class StandardPlugin extends Plugin { * @param x value at which the series is evaluated. 0 < x < 2. (x=2 is convergent but impractical.) * @return the partial sum. */ - private NumberInterface getLogPartialSum(NumberInterface x){ + private NumberInterface getLogPartialSum(NumberInterface x) { NumberInterface maxError = getMaxError(x); x = x.subtract(NaiveNumber.ONE.promoteTo(x.getClass())); //Terms used are for log(x+1). NumberInterface currentTerm = x, sum = x; int n = 1; - while(FUNCTION_ABS.apply(currentTerm).compareTo(maxError) > 0){ + while (FUNCTION_ABS.apply(currentTerm).compareTo(maxError) > 0) { n++; - currentTerm = currentTerm.multiply(x).multiply((new NaiveNumber(n-1)).promoteTo(x.getClass())).divide((new NaiveNumber(n)).promoteTo(x.getClass())).negate(); + currentTerm = currentTerm.multiply(x).multiply((new NaiveNumber(n - 1)).promoteTo(x.getClass())).divide((new NaiveNumber(n)).promoteTo(x.getClass())).negate(); sum = sum.add(currentTerm); } return sum; @@ -192,7 +180,7 @@ public class StandardPlugin extends Plugin { * @param number a number of the same type as the return type. (Used for precision.) * @return the value of log(2) with the appropriate precision. */ - private NumberInterface getLog2(NumberInterface number){ + private NumberInterface getLog2(NumberInterface number) { NumberInterface maxError = getMaxError(number); //NumberInterface errorBound = (new NaiveNumber(1)).promoteTo(number.getClass()); //We'll use the series \sigma_{n >= 1) ((1/3^n + 1/4^n) * 1/n) @@ -201,7 +189,7 @@ public class StandardPlugin extends Plugin { NumberInterface a = (new NaiveNumber(1)).promoteTo(number.getClass()), b = a, c = a; NumberInterface sum = NaiveNumber.ZERO.promoteTo(number.getClass()); int n = 0; - while(a.compareTo(maxError) >= 1){ + while (a.compareTo(maxError) >= 1) { n++; a = a.divide((new NaiveNumber(3)).promoteTo(number.getClass())); b = b.divide((new NaiveNumber(4)).promoteTo(number.getClass())); @@ -211,6 +199,17 @@ public class StandardPlugin extends Plugin { return sum; } }; + public static final Operator OP_CARET = new Operator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 2, new Function() { + @Override + protected boolean matchesParams(NumberInterface[] params) { + return params.length == 2; + } + + @Override + protected NumberInterface applyInternal(NumberInterface[] params) { + return FUNCTION_EXP.apply(FUNCTION_LN.apply(params[0]).multiply(params[1])); + } + }); public static final Function FUNCTION_SQRT = new Function() { @Override protected boolean matchesParams(NumberInterface[] params) { @@ -227,6 +226,65 @@ public class StandardPlugin extends Plugin { super(manager); } + /** + * Returns the nth term of the Taylor series (centered at 0) of e^x + * + * @param n the term required (n >= 0). + * @param x the real number at which the series is evaluated. + * @return the nth term of the series. + */ + private static NumberInterface getExpSeriesTerm(int n, NumberInterface x) { + return x.intPow(n).divide(OP_FACTORIAL.getFunction().apply((new NaiveNumber(n)).promoteTo(x.getClass()))); + } + + /** + * Returns the number of terms needed to evaluate the exponential function (at x) + * such that the error is at most maxError. + * + * @param maxError Maximum error permissible (This should probably be positive.) + * @param x where the function is evaluated. + * @return the number of terms needed to evaluated the exponential function. + */ + private static int getNTermsExp(NumberInterface maxError, NumberInterface x) { + //We need n such that |x^(n+1)| <= (n+1)! * maxError + //The variables LHS and RHS refer to the above inequality. + int n = 0; + x = FUNCTION_ABS.apply(x); + NumberInterface LHS = x, RHS = maxError; + while (LHS.compareTo(RHS) > 0) { + n++; + LHS = LHS.multiply(x); + RHS = RHS.multiply(new NaiveNumber(n + 1).promoteTo(RHS.getClass())); + } + return n; + } + + /** + * Returns a partial sum of a series whose terms are given by the nthTermFunction, evaluated at x. + * + * @param x the value at which the series is evaluated. + * @param nthTermFunction the function that returns the nth term of the series, in the format term(x, n). + * @param n the number of terms in the partial sum. + * @return the value of the partial sum that has the same class as x. + */ + private static NumberInterface sumSeries(NumberInterface x, BiFunction nthTermFunction, int n) { + NumberInterface sum = NaiveNumber.ZERO.promoteTo(x.getClass()); + for (int i = 0; i <= n; i++) { + sum = sum.add(nthTermFunction.apply(i, x)); + } + return sum; + } + + /** + * Returns the maximum error based on the precision of the class of number. + * + * @param number Any instance of the NumberInterface in question (should return an appropriate precision). + * @return the maximum error. + */ + private static NumberInterface getMaxError(NumberInterface number) { + return (new NaiveNumber(10)).promoteTo(number.getClass()).intPow(-number.getMaxPrecision()); + } + @Override public void onEnable() { registerNumber("naive", NaiveNumber.class); @@ -242,7 +300,7 @@ public class StandardPlugin extends Plugin { registerFunction("abs", FUNCTION_ABS); registerFunction("exp", FUNCTION_EXP); registerFunction("ln", FUNCTION_LN); - registerFunction("sqrt",FUNCTION_SQRT); + registerFunction("sqrt", FUNCTION_SQRT); } @Override @@ -250,60 +308,4 @@ public class StandardPlugin extends Plugin { } - /** - * Returns the nth term of the Taylor series (centered at 0) of e^x - * @param n the term required (n >= 0). - * @param x the real number at which the series is evaluated. - * @return the nth term of the series. - */ - private static NumberInterface getExpSeriesTerm(int n, NumberInterface x){ - return x.intPow(n).divide(OP_FACTORIAL.getFunction().apply((new NaiveNumber(n)).promoteTo(x.getClass()))); - } - - /** - * Returns the number of terms needed to evaluate the exponential function (at x) - * such that the error is at most maxError. - * @param maxError Maximum error permissible (This should probably be positive.) - * @param x where the function is evaluated. - * @return the number of terms needed to evaluated the exponential function. - */ - private static int getNTermsExp(NumberInterface maxError, NumberInterface x) { - //We need n such that |x^(n+1)| <= (n+1)! * maxError - //The variables LHS and RHS refer to the above inequality. - int n = 0; - x = FUNCTION_ABS.apply(x); - NumberInterface LHS = x, RHS = maxError; - while (LHS.compareTo(RHS) > 0) { - n++; - LHS = LHS.multiply(x); - RHS = RHS.multiply(new NaiveNumber(n + 1).promoteTo(RHS.getClass())); - } - return n; - } - - - /** - * Returns a partial sum of a series whose terms are given by the nthTermFunction, evaluated at x. - * @param x the value at which the series is evaluated. - * @param nthTermFunction the function that returns the nth term of the series, in the format term(x, n). - * @param n the number of terms in the partial sum. - * @return the value of the partial sum that has the same class as x. - */ - private static NumberInterface sumSeries(NumberInterface x, BiFunction nthTermFunction, int n){ - NumberInterface sum = NaiveNumber.ZERO.promoteTo(x.getClass()); - for(int i = 0; i <= n; i++){ - sum = sum.add(nthTermFunction.apply(i, x)); - } - return sum; - } - - /** - * Returns the maximum error based on the precision of the class of number. - * @param number Any instance of the NumberInterface in question (should return an appropriate precision). - * @return the maximum error. - */ - private static NumberInterface getMaxError(NumberInterface number){ - return (new NaiveNumber(10)).promoteTo(number.getClass()).intPow(-number.getMaxPrecision()); - } - } diff --git a/src/main/java/org/nwapw/abacus/tree/BinaryInfixNode.java b/src/main/java/org/nwapw/abacus/tree/BinaryInfixNode.java index f10913b..912c3ca 100644 --- a/src/main/java/org/nwapw/abacus/tree/BinaryInfixNode.java +++ b/src/main/java/org/nwapw/abacus/tree/BinaryInfixNode.java @@ -18,25 +18,28 @@ public class BinaryInfixNode extends TreeNode { */ private TreeNode right; - private BinaryInfixNode() {} + private BinaryInfixNode() { + } /** * Creates a new operation node with the given operation * and no child nodes. + * * @param operation the operation. */ - public BinaryInfixNode(String operation){ + public BinaryInfixNode(String operation) { this(operation, null, null); } /** * Creates a new operation node with the given operation * and child nodes. + * * @param operation the operation. - * @param left the left node of the expression. - * @param right the right node of the expression. + * @param left the left node of the expression. + * @param right the right node of the expression. */ - public BinaryInfixNode(String operation, TreeNode left, TreeNode right){ + public BinaryInfixNode(String operation, TreeNode left, TreeNode right) { this.operation = operation; this.left = left; this.right = right; @@ -44,6 +47,7 @@ public class BinaryInfixNode extends TreeNode { /** * Gets the operation in this node. + * * @return the operation in this node. */ public String getOperation() { @@ -52,6 +56,7 @@ public class BinaryInfixNode extends TreeNode { /** * Gets the left sub-expression of this node. + * * @return the left node. */ public TreeNode getLeft() { @@ -60,6 +65,7 @@ public class BinaryInfixNode extends TreeNode { /** * Sets the left sub-expression of this node. + * * @param left the sub-expression to apply. */ public void setLeft(TreeNode left) { @@ -68,6 +74,7 @@ public class BinaryInfixNode extends TreeNode { /** * Gets the right sub-expression of this node. + * * @return the right node. */ public TreeNode getRight() { @@ -76,6 +83,7 @@ public class BinaryInfixNode extends TreeNode { /** * Sets the right sub-expression of this node. + * * @param right the sub-expression to apply. */ public void setRight(TreeNode right) { @@ -86,7 +94,7 @@ public class BinaryInfixNode extends TreeNode { public T reduce(Reducer reducer) { T leftReduce = left.reduce(reducer); T rightReduce = right.reduce(reducer); - if(leftReduce == null || rightReduce == null) return null; + if (leftReduce == null || rightReduce == null) return null; return reducer.reduceNode(this, leftReduce, rightReduce); } diff --git a/src/main/java/org/nwapw/abacus/tree/FunctionNode.java b/src/main/java/org/nwapw/abacus/tree/FunctionNode.java index 13d7a8a..a0ee26e 100644 --- a/src/main/java/org/nwapw/abacus/tree/FunctionNode.java +++ b/src/main/java/org/nwapw/abacus/tree/FunctionNode.java @@ -20,19 +20,22 @@ public class FunctionNode extends TreeNode { /** * Creates a function node with no function. */ - private FunctionNode() { } + private FunctionNode() { + } /** * Creates a new function node with the given function name. + * * @param function the function name. */ - public FunctionNode(String function){ + public FunctionNode(String function) { this.function = function; children = new ArrayList<>(); } /** * Gets the function name for this node. + * * @return the function name. */ public String getFunction() { @@ -41,14 +44,16 @@ public class FunctionNode extends TreeNode { /** * Adds a child to the end of this node's child list. + * * @param node the child to add. */ - public void appendChild(TreeNode node){ + public void appendChild(TreeNode node) { children.add(node); } /** * Adds a new child to the beginning of this node's child list. + * * @param node the node to add. */ public void prependChild(TreeNode node) { @@ -58,9 +63,9 @@ public class FunctionNode extends TreeNode { @Override public T reduce(Reducer reducer) { Object[] reducedChildren = new Object[children.size()]; - for(int i = 0; i < reducedChildren.length; i++){ + for (int i = 0; i < reducedChildren.length; i++) { reducedChildren[i] = children.get(i).reduce(reducer); - if(reducedChildren[i] == null) return null; + if (reducedChildren[i] == null) return null; } return reducer.reduceNode(this, reducedChildren); } @@ -70,7 +75,7 @@ public class FunctionNode extends TreeNode { StringBuilder buffer = new StringBuilder(); buffer.append(function); buffer.append("("); - for(int i = 0; i < children.size(); i++){ + for (int i = 0; i < children.size(); i++) { buffer.append(children.get(i)); buffer.append(i == children.size() - 1 ? "" : ", "); } diff --git a/src/main/java/org/nwapw/abacus/tree/NumberNode.java b/src/main/java/org/nwapw/abacus/tree/NumberNode.java index 77dc7a1..14fab68 100644 --- a/src/main/java/org/nwapw/abacus/tree/NumberNode.java +++ b/src/main/java/org/nwapw/abacus/tree/NumberNode.java @@ -1,6 +1,5 @@ package org.nwapw.abacus.tree; -import org.nwapw.abacus.number.NaiveNumber; import org.nwapw.abacus.number.NumberInterface; /** @@ -16,20 +15,22 @@ public class NumberNode extends TreeNode { /** * Creates a number node with no number. */ - public NumberNode(){ + public NumberNode() { number = null; } /** * Creates a new number node with the given double value. + * * @param newNumber the number for which to create a number node. */ - public NumberNode(NumberInterface newNumber){ + public NumberNode(NumberInterface newNumber) { this.number = newNumber; } /** * Gets the number value of this node. + * * @return the number value of this node. */ public NumberInterface getNumber() { diff --git a/src/main/java/org/nwapw/abacus/tree/NumberReducer.java b/src/main/java/org/nwapw/abacus/tree/NumberReducer.java index 6fcff47..23460ed 100644 --- a/src/main/java/org/nwapw/abacus/tree/NumberReducer.java +++ b/src/main/java/org/nwapw/abacus/tree/NumberReducer.java @@ -17,34 +17,35 @@ public class NumberReducer implements Reducer { /** * Creates a new number reducer. + * * @param abacus the calculator instance. */ - public NumberReducer(Abacus abacus){ + public NumberReducer(Abacus abacus) { this.abacus = abacus; } @Override public NumberInterface reduceNode(TreeNode node, Object... children) { - if(node instanceof NumberNode) { + if (node instanceof NumberNode) { return ((NumberNode) node).getNumber(); - } else if(node instanceof BinaryInfixNode){ + } else if (node instanceof BinaryInfixNode) { NumberInterface left = (NumberInterface) children[0]; NumberInterface right = (NumberInterface) children[1]; Function function = abacus.getPluginManager().operatorFor(((BinaryInfixNode) node).getOperation()).getFunction(); - if(function == null) return null; + if (function == null) return null; return function.apply(left, right); - } else if(node instanceof UnaryPrefixNode) { + } else if (node instanceof UnaryPrefixNode) { NumberInterface child = (NumberInterface) children[0]; Function functionn = abacus.getPluginManager().operatorFor(((UnaryPrefixNode) node).getOperation()).getFunction(); - if(functionn == null) return null; + if (functionn == null) return null; return functionn.apply(child); - } else if(node instanceof FunctionNode){ + } else if (node instanceof FunctionNode) { NumberInterface[] convertedChildren = new NumberInterface[children.length]; - for(int i = 0; i < convertedChildren.length; i++){ + for (int i = 0; i < convertedChildren.length; i++) { convertedChildren[i] = (NumberInterface) children[i]; } Function function = abacus.getPluginManager().functionFor(((FunctionNode) node).getFunction()); - if(function == null) return null; + if (function == null) return null; return function.apply(convertedChildren); } return null; diff --git a/src/main/java/org/nwapw/abacus/tree/Reducer.java b/src/main/java/org/nwapw/abacus/tree/Reducer.java index 966478d..b7bbdbd 100644 --- a/src/main/java/org/nwapw/abacus/tree/Reducer.java +++ b/src/main/java/org/nwapw/abacus/tree/Reducer.java @@ -2,16 +2,18 @@ package org.nwapw.abacus.tree; /** * Interface used to reduce a tree into a single value. + * * @param the value to reduce into. */ public interface Reducer { /** * Reduces the given tree into a single value of type T. - * @param node the node being passed in to be reduced. + * + * @param node the node being passed in to be reduced. * @param children the already-reduced children of this node. * @return the resulting value from the reduce. */ - public T reduceNode(TreeNode node, Object...children); + public T reduceNode(TreeNode node, Object... children); } diff --git a/src/main/java/org/nwapw/abacus/tree/TokenType.java b/src/main/java/org/nwapw/abacus/tree/TokenType.java index b5629b5..ebb8861 100644 --- a/src/main/java/org/nwapw/abacus/tree/TokenType.java +++ b/src/main/java/org/nwapw/abacus/tree/TokenType.java @@ -16,9 +16,10 @@ public enum TokenType { /** * Creates a new token type with the given priority. + * * @param priority the priority of this token type. */ - TokenType(int priority){ + TokenType(int priority) { this.priority = priority; } diff --git a/src/main/java/org/nwapw/abacus/tree/TreeNode.java b/src/main/java/org/nwapw/abacus/tree/TreeNode.java index 50d8991..d98e638 100644 --- a/src/main/java/org/nwapw/abacus/tree/TreeNode.java +++ b/src/main/java/org/nwapw/abacus/tree/TreeNode.java @@ -1,11 +1,5 @@ package org.nwapw.abacus.tree; -import org.nwapw.abacus.function.OperatorAssociativity; -import org.nwapw.abacus.lexing.Lexer; -import org.nwapw.abacus.lexing.pattern.Match; - -import java.util.*; - /** * An abstract class that represents an expression tree node. */ @@ -13,8 +7,9 @@ public abstract class TreeNode { /** * The function that reduces a tree to a single vale. + * * @param reducer the reducer used to reduce the tree. - * @param the type the reducer produces. + * @param the type the reducer produces. * @return the result of the reduction, or null on error. */ public abstract T reduce(Reducer reducer); diff --git a/src/main/java/org/nwapw/abacus/tree/UnaryPrefixNode.java b/src/main/java/org/nwapw/abacus/tree/UnaryPrefixNode.java index dce46c5..2c04203 100644 --- a/src/main/java/org/nwapw/abacus/tree/UnaryPrefixNode.java +++ b/src/main/java/org/nwapw/abacus/tree/UnaryPrefixNode.java @@ -13,18 +13,20 @@ public class UnaryPrefixNode extends TreeNode { /** * Creates a new node with the given operation and no child. + * * @param operation the operation for this node. */ - public UnaryPrefixNode(String operation){ + public UnaryPrefixNode(String operation) { this(operation, null); } /** * Creates a new node with the given operation and child. + * * @param operation the operation for this node. - * @param applyTo the node to apply the function to. + * @param applyTo the node to apply the function to. */ - public UnaryPrefixNode(String operation, TreeNode applyTo){ + public UnaryPrefixNode(String operation, TreeNode applyTo) { this.operation = operation; this.applyTo = applyTo; } @@ -32,12 +34,13 @@ public class UnaryPrefixNode extends TreeNode { @Override public T reduce(Reducer reducer) { Object reducedChild = applyTo.reduce(reducer); - if(reducedChild == null) return null; + if (reducedChild == null) return null; return reducer.reduceNode(this, reducedChild); } /** * Gets the operation of this node. + * * @return the operation this node performs. */ public String getOperation() { @@ -46,6 +49,7 @@ public class UnaryPrefixNode extends TreeNode { /** * Gets the node to which this node's operation applies. + * * @return the tree node to which the operation will be applied. */ public TreeNode getApplyTo() { diff --git a/src/main/java/org/nwapw/abacus/window/HistoryTableModel.java b/src/main/java/org/nwapw/abacus/window/HistoryTableModel.java index 323e209..88106d0 100644 --- a/src/main/java/org/nwapw/abacus/window/HistoryTableModel.java +++ b/src/main/java/org/nwapw/abacus/window/HistoryTableModel.java @@ -2,9 +2,7 @@ package org.nwapw.abacus.window; import org.nwapw.abacus.tree.TreeNode; -import javax.swing.event.TableModelListener; import javax.swing.table.AbstractTableModel; -import javax.swing.table.TableModel; import java.util.ArrayList; import java.util.List; @@ -31,30 +29,6 @@ public class HistoryTableModel extends AbstractTableModel { TreeNode.class, String.class }; - - /** - * Class used specifically to hold data about - * the previous entries into the calculator. - */ - public static class HistoryEntry { - public String input; - public TreeNode parsedInput; - public String output; - - public HistoryEntry(String input, TreeNode parsedInput, String output){ - this.input = input; - this.parsedInput = parsedInput; - this.output = output; - } - - Object nthValue(int n){ - if(n == 0) return input; - if(n == 1) return parsedInput; - if(n == 2) return output; - return null; - } - } - /** * The list of entries. */ @@ -69,9 +43,10 @@ public class HistoryTableModel extends AbstractTableModel { /** * Adds an entry to the model. + * * @param entry the entry to add. */ - public void addEntry(HistoryEntry entry){ + public void addEntry(HistoryEntry entry) { entries.add(entry); } @@ -105,4 +80,27 @@ public class HistoryTableModel extends AbstractTableModel { return entries.get(rowIndex).nthValue(columnIndex); } + /** + * Class used specifically to hold data about + * the previous entries into the calculator. + */ + public static class HistoryEntry { + public String input; + public TreeNode parsedInput; + public String output; + + public HistoryEntry(String input, TreeNode parsedInput, String output) { + this.input = input; + this.parsedInput = parsedInput; + this.output = output; + } + + Object nthValue(int n) { + if (n == 0) return input; + if (n == 1) return parsedInput; + if (n == 2) return output; + return null; + } + } + } diff --git a/src/main/java/org/nwapw/abacus/window/Window.java b/src/main/java/org/nwapw/abacus/window/Window.java index 0484cf2..40081ea 100644 --- a/src/main/java/org/nwapw/abacus/window/Window.java +++ b/src/main/java/org/nwapw/abacus/window/Window.java @@ -119,12 +119,12 @@ public class Window extends JFrame { */ private ActionListener evaluateListener = (event) -> { TreeNode parsedExpression = abacus.parseString(inputField.getText()); - if(parsedExpression == null){ + if (parsedExpression == null) { lastOutputArea.setText(SYNTAX_ERR_STRING); return; } NumberInterface numberInterface = abacus.evaluateTree(parsedExpression); - if(numberInterface == null) { + if (numberInterface == null) { lastOutputArea.setText(EVAL_ERR_STRING); return; } @@ -146,9 +146,10 @@ public class Window extends JFrame { /** * Creates a new window with the given manager. + * * @param abacus the calculator instance to interact with other components. */ - public Window(Abacus abacus){ + public Window(Abacus abacus) { this(); this.abacus = abacus; } @@ -218,11 +219,11 @@ public class Window extends JFrame { inputField.setEnabled(enabled); inputEnterButton.setEnabled(enabled); - for(ActionListener removingListener : inputEnterButton.getActionListeners()){ + for (ActionListener removingListener : inputEnterButton.getActionListeners()) { inputEnterButton.removeActionListener(removingListener); inputField.removeActionListener(removingListener); } - if(listener != null){ + if (listener != null) { inputEnterButton.addActionListener(listener); inputField.addActionListener(listener); } @@ -236,7 +237,7 @@ public class Window extends JFrame { @Override public void mouseClicked(MouseEvent e) { Point clickPoint = e.getPoint(); - if(e.getClickCount() == 2){ + if (e.getClickCount() == 2) { int row = historyTable.rowAtPoint(clickPoint); int column = historyTable.columnAtPoint(clickPoint); String toCopy = historyTable.getValueAt(row, column).toString(); diff --git a/src/test/java/org/nwapw/abacus/tests/LexerTests.java b/src/test/java/org/nwapw/abacus/tests/LexerTests.java index 268a06c..80a8dfb 100644 --- a/src/test/java/org/nwapw/abacus/tests/LexerTests.java +++ b/src/test/java/org/nwapw/abacus/tests/LexerTests.java @@ -10,7 +10,7 @@ import java.util.List; public class LexerTests { @Test - public void testBasicSuccess(){ + public void testBasicSuccess() { Lexer lexer = new Lexer<>(); lexer.register("abc", 0); lexer.register("def", 1); @@ -22,7 +22,7 @@ public class LexerTests { } @Test - public void testBasicFailure(){ + public void testBasicFailure() { Lexer lexer = new Lexer<>(); lexer.register("abc", 0); lexer.register("def", 1); @@ -30,20 +30,20 @@ public class LexerTests { } @Test - public void testNoPatterns(){ + public void testNoPatterns() { Lexer lexer = new Lexer<>(); Assert.assertNull(lexer.lexAll("abcdefabc", 0, Integer::compare)); } @Test - public void testEmptyMatches(){ + public void testEmptyMatches() { Lexer lexer = new Lexer<>(); lexer.register("a?", 0); Assert.assertNull(lexer.lexAll("", 0, Integer::compare)); } @Test - public void testOneOrMore(){ + public void testOneOrMore() { Lexer lexer = new Lexer<>(); lexer.register("a+", 0); List> tokens = lexer.lexAll("aaaa", 0, Integer::compare); @@ -52,7 +52,7 @@ public class LexerTests { } @Test - public void testZeroOrMore(){ + public void testZeroOrMore() { Lexer lexer = new Lexer<>(); lexer.register("a*", 0); List> tokens = lexer.lexAll("aaaa", 0, Integer::compare); @@ -61,7 +61,7 @@ public class LexerTests { } @Test - public void testZeroOrOne(){ + public void testZeroOrOne() { Lexer lexer = new Lexer<>(); lexer.register("a?", 0); List> tokens = lexer.lexAll("aaaa", 0, Integer::compare); @@ -70,7 +70,7 @@ public class LexerTests { } @Test - public void testGreedyMatching(){ + public void testGreedyMatching() { Lexer lexer = new Lexer<>(); lexer.register("a*a", 0); List> tokens = lexer.lexAll("aaaa", 0, Integer::compare); @@ -79,20 +79,20 @@ public class LexerTests { } @Test - public void testAnyCharacter(){ + public void testAnyCharacter() { String testString = "abcdef"; Lexer lexer = new Lexer<>(); lexer.register(".", 0); List> tokens = lexer.lexAll(testString, 0, Integer::compare); Assert.assertNotNull(tokens); Assert.assertEquals(tokens.size(), testString.length()); - for(int i = 0; i < tokens.size(); i++){ + for (int i = 0; i < tokens.size(); i++) { Assert.assertEquals(testString.substring(i, i + 1), tokens.get(i).getContent()); } } @Test - public void testBasicGroup(){ + public void testBasicGroup() { Lexer lexer = new Lexer<>(); lexer.register("(abc)", 0); List> tokens = lexer.lexAll("abc", 0, Integer::compare); @@ -102,27 +102,27 @@ public class LexerTests { } @Test - public void testBasicRangeSuccess(){ + public void testBasicRangeSuccess() { String testString = "abcdef"; Lexer lexer = new Lexer<>(); lexer.register("[a-f]", 0); List> tokens = lexer.lexAll(testString, 0, Integer::compare); Assert.assertNotNull(tokens); Assert.assertEquals(testString.length(), tokens.size()); - for(int i = 0; i < tokens.size(); i++){ + for (int i = 0; i < tokens.size(); i++) { Assert.assertEquals(testString.substring(i, i + 1), tokens.get(i).getContent()); } } @Test - public void testBasicRangeFailure(){ + public void testBasicRangeFailure() { Lexer lexer = new Lexer<>(); lexer.register("[a-f]", 0); Assert.assertNull(lexer.lexAll("g", 0, Integer::compare)); } - + @Test - public void testGroupAndOperator(){ + public void testGroupAndOperator() { Lexer lexer = new Lexer<>(); lexer.register("(abc)+", 0); List> tokens = lexer.lexAll("abcabc", 0, Integer::compare); diff --git a/src/test/java/org/nwapw/abacus/tests/TokenizerTests.java b/src/test/java/org/nwapw/abacus/tests/TokenizerTests.java index ecdda23..7de1ec0 100644 --- a/src/test/java/org/nwapw/abacus/tests/TokenizerTests.java +++ b/src/test/java/org/nwapw/abacus/tests/TokenizerTests.java @@ -64,17 +64,17 @@ public class TokenizerTests { @Test public void testInteger() { - assertTokensMatch(lexerTokenizer.tokenizeString("11"), new TokenType[]{ TokenType.NUM } ); + assertTokensMatch(lexerTokenizer.tokenizeString("11"), new TokenType[]{TokenType.NUM}); } @Test public void testLeadingZeroDecimal() { - assertTokensMatch(lexerTokenizer.tokenizeString("0.1"), new TokenType[]{ TokenType.NUM } ); + assertTokensMatch(lexerTokenizer.tokenizeString("0.1"), new TokenType[]{TokenType.NUM}); } @Test public void testNonLeadingDecimal() { - assertTokensMatch(lexerTokenizer.tokenizeString(".1"), new TokenType[]{ TokenType.NUM } ); + assertTokensMatch(lexerTokenizer.tokenizeString(".1"), new TokenType[]{TokenType.NUM}); } @Test @@ -102,7 +102,7 @@ public class TokenizerTests { } @Test - public void testOperatorParsing(){ + public void testOperatorParsing() { TokenType[] types = { TokenType.NUM, TokenType.OP, @@ -112,7 +112,7 @@ public class TokenizerTests { } @Test - public void testSanitizedOperators(){ + public void testSanitizedOperators() { TokenType[] types = { TokenType.NUM, TokenType.OP,