diff --git a/src/org/nwapw/abacus/function/Function.java b/src/org/nwapw/abacus/function/Function.java index bd5003d..103c3f4 100755 --- a/src/org/nwapw/abacus/function/Function.java +++ b/src/org/nwapw/abacus/function/Function.java @@ -5,16 +5,40 @@ import org.nwapw.abacus.number.NumberInterface; import java.util.HashMap; +/** + * A function that operates on one or more + * inputs and returns a single number. + */ public abstract class Function { + /** + * A map to correctly promote different number implementations to each other. + */ private static final HashMap, Integer> priorityMap = new HashMap, Integer>() {{ put(NaiveNumber.class, 0); }}; + /** + * 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. + */ protected abstract boolean matchesParams(NumberInterface[] params); + + /** + * 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. + */ protected abstract NumberInterface applyInternal(NumberInterface[] params); + /** + * 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; return applyInternal(params); diff --git a/src/org/nwapw/abacus/lexing/Lexer.java b/src/org/nwapw/abacus/lexing/Lexer.java index 94457a1..2813061 100644 --- a/src/org/nwapw/abacus/lexing/Lexer.java +++ b/src/org/nwapw/abacus/lexing/Lexer.java @@ -9,19 +9,42 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.HashSet; +/** + * 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 ArrayList> patterns; + /** + * Creates a new lexer with no registered patterns. + */ public Lexer(){ patterns = new ArrayList<>(); } + /** + * 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.add(compiledPattern); } + /** + * 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<>(); @@ -53,6 +76,13 @@ public class Lexer { 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 ArrayList> lexAll(String from, int startAt, Comparator compare){ int index = startAt; ArrayList> matches = new ArrayList<>(); diff --git a/src/org/nwapw/abacus/lexing/pattern/AnyNode.java b/src/org/nwapw/abacus/lexing/pattern/AnyNode.java index 1cfa635..5b75e51 100644 --- a/src/org/nwapw/abacus/lexing/pattern/AnyNode.java +++ b/src/org/nwapw/abacus/lexing/pattern/AnyNode.java @@ -1,5 +1,9 @@ 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 { @Override diff --git a/src/org/nwapw/abacus/lexing/pattern/EndNode.java b/src/org/nwapw/abacus/lexing/pattern/EndNode.java index d8e621d..81be4cd 100644 --- a/src/org/nwapw/abacus/lexing/pattern/EndNode.java +++ b/src/org/nwapw/abacus/lexing/pattern/EndNode.java @@ -1,13 +1,28 @@ 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 { + /** + * The ID of the pattenr that has been matched. + */ private T patternId; + /** + * Creates a new end node with the given ID. + * @param patternId the pattern ID. + */ public EndNode(T patternId){ this.patternId = patternId; } + /** + * Gets the pattern ID. + * @return the pattern ID. + */ public T getPatternId(){ return patternId; } diff --git a/src/org/nwapw/abacus/lexing/pattern/LinkNode.java b/src/org/nwapw/abacus/lexing/pattern/LinkNode.java index 460c418..7c85c0f 100644 --- a/src/org/nwapw/abacus/lexing/pattern/LinkNode.java +++ b/src/org/nwapw/abacus/lexing/pattern/LinkNode.java @@ -3,6 +3,10 @@ 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 diff --git a/src/org/nwapw/abacus/lexing/pattern/Match.java b/src/org/nwapw/abacus/lexing/pattern/Match.java index 06e0b27..15ee33d 100644 --- a/src/org/nwapw/abacus/lexing/pattern/Match.java +++ b/src/org/nwapw/abacus/lexing/pattern/Match.java @@ -1,25 +1,56 @@ 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 { + /** + * The bottom range of the string, inclusive. + */ private int from; + /** + * The top range of the string, exclusive. + */ private int to; + /** + * The pattern type this match matched. + */ private T type; + /** + * Creates a new match with the given parameters. + * @param from the bottom range of the string. + * @param to the top range of the string. + * @param type the type of the match. + */ public Match(int from, int to, T type){ this.from = from; this.to = to; this.type = type; } + /** + * Gets the bottom range bound of the string. + * @return the bottom range bound of the string. + */ public int getFrom() { return from; } + /** + * Gets the top range bound of the string. + * @return the top range bound of the string. + */ public int getTo() { return to; } + /** + * Gets the pattern type of the node. + * @return the ID of the pattern that this match matched. + */ public T getType() { return type; } diff --git a/src/org/nwapw/abacus/lexing/pattern/Pattern.java b/src/org/nwapw/abacus/lexing/pattern/Pattern.java index 4d40d77..2057a8c 100644 --- a/src/org/nwapw/abacus/lexing/pattern/Pattern.java +++ b/src/org/nwapw/abacus/lexing/pattern/Pattern.java @@ -5,13 +5,33 @@ import java.util.HashMap; import java.util.Stack; 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 { + /** + * The ID of this pattern. + */ private T id; + /** + * The head of this pattern. + */ private PatternNode head; + /** + * The source string of this pattern. + */ private String source; + /** + * The index at which the compilation has stopped. + */ private int index; + /** + * A map of regex operator to functions that modify a PatternChain + * with the appropriate operation. + */ private HashMap, PatternChain>> operations = new HashMap, PatternChain>>() {{ put('+', Pattern.this::transformPlus); @@ -19,11 +39,23 @@ public class Pattern { put('?', Pattern.this::transformQuestion); }}; + /** + * 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){ chain.tail.outputStates.add(chain.head); return chain; } + /** + * 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){ LinkNode newTail = new LinkNode<>(); LinkNode newHead = new LinkNode<>(); @@ -36,6 +68,12 @@ public class Pattern { return chain; } + /** + * 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){ LinkNode newTail = new LinkNode<>(); LinkNode newHead = new LinkNode<>(); @@ -47,6 +85,11 @@ public class Pattern { return chain; } + /** + * 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){ LinkNode head = new LinkNode<>(); LinkNode tail = new LinkNode<>(); @@ -58,6 +101,10 @@ public class Pattern { return newChain; } + /** + * 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) == '\\'){ @@ -66,6 +113,10 @@ public class Pattern { 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(){ Stack> orStack = new Stack<>(); index++; @@ -88,6 +139,12 @@ public class Pattern { 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++; @@ -152,6 +209,11 @@ 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; @@ -166,6 +228,10 @@ public class Pattern { } } + /** + * Gets the head PatternNode, for use in matching + * @return the pattern node. + */ public PatternNode getHead() { return head; } diff --git a/src/org/nwapw/abacus/lexing/pattern/PatternChain.java b/src/org/nwapw/abacus/lexing/pattern/PatternChain.java index aad9be5..a6edb21 100644 --- a/src/org/nwapw/abacus/lexing/pattern/PatternChain.java +++ b/src/org/nwapw/abacus/lexing/pattern/PatternChain.java @@ -1,23 +1,52 @@ 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 { + /** + * The head node of the chain. + */ public PatternNode head; + /** + * The tail node of the chain. + */ public PatternNode tail; + /** + * 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){ 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){ this(node, node); } + /** + * Creates an empty chain. + */ public PatternChain(){ this(null); } + /** + * 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) { this.head = other.head; @@ -28,6 +57,12 @@ 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){ head = tail = node; diff --git a/src/org/nwapw/abacus/lexing/pattern/PatternNode.java b/src/org/nwapw/abacus/lexing/pattern/PatternNode.java index 4c0908a..51208db 100644 --- a/src/org/nwapw/abacus/lexing/pattern/PatternNode.java +++ b/src/org/nwapw/abacus/lexing/pattern/PatternNode.java @@ -4,26 +4,58 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +/** + * 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 { + /** + * The set of states to which the lexer should continue + * should this node be correctly matched. + */ protected HashSet> outputStates; + /** + * Creates a new pattern node. + */ 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){ 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(){ return '\0'; } + /** + * Adds this node in a collection of other nodes. + * @param into the collection to add 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){ outputStates.forEach(e -> e.addInto(into)); } diff --git a/src/org/nwapw/abacus/lexing/pattern/RangeNode.java b/src/org/nwapw/abacus/lexing/pattern/RangeNode.java index 4ff9e5b..1eae44c 100644 --- a/src/org/nwapw/abacus/lexing/pattern/RangeNode.java +++ b/src/org/nwapw/abacus/lexing/pattern/RangeNode.java @@ -1,10 +1,25 @@ 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 { + /** + * The bottom bound of the range, inclusive. + */ private char from; + /** + * The top bound of the range, inclusive. + */ private char to; + /** + * 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. + */ public RangeNode(char from, char to){ this.from = from; this.to = to; diff --git a/src/org/nwapw/abacus/lexing/pattern/ValueNode.java b/src/org/nwapw/abacus/lexing/pattern/ValueNode.java index 855f805..b3268ac 100644 --- a/src/org/nwapw/abacus/lexing/pattern/ValueNode.java +++ b/src/org/nwapw/abacus/lexing/pattern/ValueNode.java @@ -1,9 +1,20 @@ 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 { + /** + * The value this node matches. + */ private char value; + /** + * Creates a new node that matches the given character. + * @param value + */ public ValueNode(char value){ this.value = value; } diff --git a/src/org/nwapw/abacus/number/NaiveNumber.java b/src/org/nwapw/abacus/number/NaiveNumber.java index 6ad43f1..585f7f1 100755 --- a/src/org/nwapw/abacus/number/NaiveNumber.java +++ b/src/org/nwapw/abacus/number/NaiveNumber.java @@ -1,14 +1,30 @@ package org.nwapw.abacus.number; +/** + * An implementation of NumberInterface using a double. + */ public class NaiveNumber implements NumberInterface { + /** + * The value of this number. + */ private double 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. + */ public static final NaiveNumber ZERO = new NaiveNumber(0); + /** + * The number one. + */ public static final NaiveNumber ONE = new NaiveNumber(1); @Override diff --git a/src/org/nwapw/abacus/number/NumberInterface.java b/src/org/nwapw/abacus/number/NumberInterface.java index e9cf59b..85f2214 100755 --- a/src/org/nwapw/abacus/number/NumberInterface.java +++ b/src/org/nwapw/abacus/number/NumberInterface.java @@ -1,17 +1,77 @@ package org.nwapw.abacus.number; +/** + * An interface used to represent a number. + */ public interface NumberInterface { + /** + * The precision to which this number operates. + * @return the precision. + */ int precision(); + + /** + * 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. + */ NumberInterface subtract(NumberInterface subtrahend); + + /** + * 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 + * @return + */ NumberInterface intPow(int exponent); + + /** + * Compares this number to another. + * @param number the number to compare to. + * @return same as Integer.compare(); + */ int compareTo(NumberInterface number); + + /** + * 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. + */ NumberInterface promoteTo(Class toClass); }