1
0
mirror of https://github.com/DanilaFe/abacus synced 2026-01-25 16:15:19 +00:00

Compare commits

...

74 Commits

Author SHA1 Message Date
cc2da711e7 Bump Gradle, Kotlin, and toml4j versions.
Apparently, Gradle was incompatible with Java 10, and neither was
toml4j.
2018-05-18 15:19:53 -07:00
ef39bcbaa2 Merge pull request #55 from DanilaFe/unary-minus-fix
Fix bug causing incorrect parsing of inputs with negative signs.
2018-01-30 21:12:29 -08:00
ac17246317 Fix bug causing incorrect parsing of inputs with negative signs. 2018-01-30 21:10:02 -08:00
18e0bdebc5 Merge pull request #54 from DanilaFe/precedence-fix
Make sure the set and define operators have the lowest precedence.
2018-01-16 00:16:46 -08:00
251d0fc2c5 Make sure the set and define operators have the lowest precedence. 2018-01-16 00:14:46 -08:00
93c1c53612 Merge pull request #53 from DanilaFe/definitions-bugfix
Load definitions after Abacus finishes loading.
2017-11-23 23:02:25 -08:00
0b6798e28d Load definitions after Abacus finishes loading. 2017-11-23 23:00:33 -08:00
4188c83a66 Merge pull request #52 from DanilaFe/definition-files
Allow loading definition files.
2017-11-23 21:56:17 -08:00
af988a34d1 Merge branch 'master' into definition-files 2017-11-23 21:45:41 -08:00
751fd97fcb Merge pull request #51 from DanilaFe/context-adjustments
Add an additional, restricted type of context for use only by plugins.
2017-11-23 21:43:37 -08:00
899e0e65a5 Make definition file addition / removal trigger the change flag. 2017-11-23 21:11:03 -08:00
b4b64ac963 Make UI adjustments. 2017-11-23 21:09:23 -08:00
50cc51d089 Load definition files. 2017-11-23 20:45:24 -08:00
8f682e96af Add UI to add definition files. 2017-11-23 20:15:39 -08:00
9bdf188ca7 Add loading of files to the core, but not the UI. 2017-11-23 19:52:18 -08:00
9fdb3fc0d9 Add an additional, restricted type of context for use only by plugins. 2017-11-23 13:29:06 -08:00
095d374949 Merge pull request #47 from DanilaFe/plugin-conversions
Move all the number reducer functionality into the context.
2017-11-14 23:22:01 -08:00
981211ccfa Merge branch 'master' into plugin-conversions 2017-11-14 23:18:18 -08:00
d91536e746 Merge pull request #48 from DanilaFe/documentation-fix
Fix crash when a TreeValueFunction doesn't have documentation loaded.
2017-11-14 23:18:09 -08:00
7c252fddf1 Merge branch 'master' into plugin-conversions 2017-11-14 23:13:51 -08:00
0d26167f31 Merge branch 'master' into documentation-fix 2017-11-14 23:13:44 -08:00
07abe4d17a Merge pull request #46 from DanilaFe/isint-idiom
Add and use isInteger function where appropriate.
2017-11-14 23:13:10 -08:00
8ef0904d26 Fix crash when a TreeValueFunction doesn't have documentation loaded. 2017-11-14 23:08:04 -08:00
92489551ca Move all the number reducer functionality into the context. 2017-11-14 23:03:12 -08:00
2a9026f748 Add and use isInteger function where appropriate. 2017-11-03 21:41:46 -07:00
08e5b69c04 Merge pull request #43 from DanilaFe/java8-subset
Replace some recent android API features with backwards compatible ones.
2017-09-24 13:04:23 -07:00
6a0b667c32 Replace some recent android API features with backwards compatible ones. 2017-09-24 12:58:33 -07:00
5a1fdfe4bc Merge pull request #42 from DanilaFe/java8-subset
Restrict Abacus core to a Java8 subset
2017-09-24 00:38:54 -07:00
378ff946d9 Add a Transformation class that replaces java.util.function use 2017-09-24 00:29:43 -07:00
0511c58b13 Remove the Pattern's dependency on java.util.function 2017-09-24 00:12:25 -07:00
e82a13cde5 Move ClassFinder to the fx module, which is the only place it's used. 2017-09-24 00:01:43 -07:00
40362a7afe Merge pull request #40 from DanilaFe/move-files
More around files into more applicable packages.
2017-09-23 23:46:02 -07:00
c990d4c50a More around files into more applicable packages. 2017-09-23 23:43:08 -07:00
d7bb838866 Merge pull request #39 from DanilaFe/more-kotlin
Switch more code to Kotlin
2017-09-23 23:03:23 -07:00
e05b2ac8d5 Move the StandardPlugin into a "standard" package along with the ops. 2017-09-23 22:16:44 -07:00
b4214f5714 Rewrite the parsing interfaces in Kotlin. 2017-09-23 17:14:55 -07:00
bd02749706 Move all the operators into a separate subpackage. 2017-09-23 17:13:56 -07:00
f809183126 Move some more operators out of StandardPlugin.java to separate classes 2017-09-23 17:11:44 -07:00
579ff78a99 Move two more operators into separate classes. 2017-09-23 16:19:45 -07:00
81d0999c11 Switch the basic operators into individual classes. 2017-09-23 15:54:28 -07:00
4fd8f7badf Add operator overloading. 2017-09-23 15:54:28 -07:00
bc475a22f9 Rewrite NumberInterface in Kotlin. 2017-09-23 15:54:28 -07:00
e0ccb67ad3 Merge pull request #37 from DanilaFe/more-exceptions
Replace some more cases where null is used with Exceptions.
2017-09-23 15:53:37 -07:00
ea4588be44 Add more descriptive message to context exceptions. 2017-09-23 15:43:07 -07:00
31996219ad Switch the Lexer and TreeBuilder to using exceptions. 2017-09-23 15:31:35 -07:00
a3bfc34c1c Throw parse exceptions instead of returning null. 2017-09-22 16:35:08 -07:00
8dc7acd4b3 Add a separate class of exceptions for NumberReducer. 2017-09-22 11:58:19 -07:00
76fcd8ec1c Remove unused elvis. 2017-09-21 23:17:45 -07:00
fbdf2c7e52 Eliminate warnings related to null returns that have been removed. 2017-09-21 23:09:13 -07:00
3057f66e66 Throw the exception instead of returning null. 2017-09-21 23:05:48 -07:00
f385a48aa2 Merge pull request #34 from DanilaFe/number-range
Add a NumberRange utility.
2017-09-20 15:42:05 -07:00
fd3f56aa8f Write some tests for the Ranges. 2017-09-20 15:23:17 -07:00
e364f4e94b Have the NumberInterface provide the Kotlin rangeTo method. 2017-09-20 13:22:18 -07:00
4c94abb18b Create the NumberRangeBuilder utility class. 2017-09-20 13:19:55 -07:00
ba63dd7874 Implement a range that works for NumberInterfaces. 2017-09-20 13:04:20 -07:00
566598b702 Merge pull request #33 from DanilaFe/promotion-fix
Fix a bug that made some same-priority implementations not convert.
2017-09-20 12:56:12 -07:00
eb91a5b875 Fix a bug that made some same-priority implementations not convert. 2017-09-20 12:47:30 -07:00
fcd4694203 Merge pull request #32 from DanilaFe/promotion-exception
Add an exception thrown when promotion fails.
2017-09-20 12:16:00 -07:00
566831246c Add an exception thrown when promotion fails. 2017-09-20 12:06:06 -07:00
ad8a0a9b2a Merge pull request #29 from DanilaFe/fixes
Revert "Remove unnecessary nullability from parseString."
2017-09-16 03:05:20 -07:00
e430e738cf Merge branch 'master' into fixes 2017-09-16 03:03:52 -07:00
f6e326e0f1 Revert "Remove unnecessary nullability from parseString."
88e3bb7109
2017-09-16 03:02:35 -07:00
07581557c7 Merge pull request #27 from DanilaFe/fixes
Fix a number of small issues not worthy of their own branches.
2017-09-16 01:26:40 -07:00
14ac9c67f4 Implement the comparable interface. 2017-09-16 00:18:43 -07:00
0ff071e212 Add a more complete .gitignore 2017-09-16 00:17:03 -07:00
88e3bb7109 Remove unnecessary nullability from parseString. 2017-09-16 00:16:48 -07:00
540e5d6099 Load default implementation if one is not found. 2017-09-16 00:16:32 -07:00
c9e93d87a2 Merge pull request #23 from DanilaFe/thread-safety
Make some parts of the code more thread safe.
2017-09-15 22:59:42 -07:00
337edd68fa Merge branch 'master' into thread-safety 2017-09-15 22:58:33 -07:00
08967fbb8f Merge pull request #22 from DanilaFe/less-null
Remove some null-heavy parts of the code.
2017-09-15 22:56:54 -07:00
46f78bb2ed Merge pull request #21 from DanilaFe/context
Implement a Context system which allows concurrent creation of variables.
2017-09-15 22:51:56 -07:00
87529da15f Precompute all the transitions ahead of time. 2017-09-11 19:32:51 -07:00
7cd117dac1 Add synchronization to the Standard plugin. 2017-09-11 19:32:51 -07:00
8975bfdb99 Precompute Pi, and do not store documentation on access. 2017-09-11 19:32:51 -07:00
89 changed files with 1486 additions and 882 deletions

4
.gitignore vendored
View File

@@ -24,7 +24,9 @@ hs_err_pid*
# Custom Stuff # Custom Stuff
# Gradle # Gradle
.gradle/* .gradle/*
build/* **/build/*
**/out/**
**/.DS_Store
# IntelliJ # IntelliJ
.idea/* .idea/*

View File

@@ -1,6 +1,6 @@
buildscript { buildscript {
ext.kotlin_version = '1.1.3' ext.kotlin_version = '1.2.40'
ext.dokka_version = '0.9.15' ext.dokka_version = '0.9.16'
repositories { repositories {
jcenter() jcenter()
@@ -23,7 +23,7 @@ subprojects {
} }
dependencies { dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:1.1.3" compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.40"
} }
} }

View File

@@ -0,0 +1,24 @@
package org.nwapw.abacus.exception;
/**
* Exception thrown by the Context in cases where lookup fails
* where it should not.
*/
public class ContextException extends AbacusException {
/**
* Creates a new ContextException without an extra message.
*/
public ContextException() {
this("");
}
/**
* Creates a ContextException with the given message.
* @param message the message to use.
*/
public ContextException(String message){
super("Context exception", message);
}
}

View File

@@ -0,0 +1,25 @@
package org.nwapw.abacus.exception;
/**
* Exception thrown by the NumberReducer if something goes wrong when
* transforming a parse tree into a single value.
*/
public class NumberReducerException extends AbacusException {
/**
* Creates a new NumberReducerException with
* no additional message.
*/
public NumberReducerException() {
this("");
}
/**
* Creates a new NumberReducerException with the given message.
* @param message the message.
*/
public NumberReducerException(String message) {
super("Error evaluating expression", message);
}
}

View File

@@ -0,0 +1,23 @@
package org.nwapw.abacus.exception;
/**
* Exception thrown by parsers.
*/
public class ParseException extends AbacusException {
/**
* Creates a new ParseException with no additional message.
*/
public ParseException(){
this("");
}
/**
* Creates a new ParseException with the given additional message.
* @param message the message.
*/
public ParseException(String message){
super("Failed to parse string", message);
}
}

View File

@@ -0,0 +1,24 @@
package org.nwapw.abacus.exception;
/**
* Exception thrown when a promotion fails.
*/
public class PromotionException extends AbacusException {
/**
* Creates a new PromotionException with the default message
* and no additional information.
*/
public PromotionException() {
this("");
}
/**
* Creates a new PromotionException with the given additional message.
* @param message the additional message to include with the error.
*/
public PromotionException(String message) {
super("Failed to promote number instances", message);
}
}

View File

@@ -1,16 +1,14 @@
package org.nwapw.abacus.exception; package org.nwapw.abacus.exception;
/** /**
* An exception thrown primarily from Tree Value operators and functions, * An exception thrown from TreeReducers.
* which have to deal with the result of a Reducer as well as the results
* of Applicable.
*/ */
public class EvaluationException extends AbacusException { public class ReductionException extends AbacusException {
/** /**
* Creates a new EvaluationException with the default string. * Creates a new EvaluationException with the default string.
*/ */
public EvaluationException() { public ReductionException() {
this(""); this("");
} }
@@ -18,7 +16,7 @@ public class EvaluationException extends AbacusException {
* Creates a new EvaluationError with the given message string. * Creates a new EvaluationError with the given message string.
* @param message the message string. * @param message the message string.
*/ */
public EvaluationException(String message) { public ReductionException(String message) {
super("Evaluation error", message); super("Evaluation error", message);
} }

View File

@@ -0,0 +1,23 @@
package org.nwapw.abacus.exception;
/**
* Exception thrown by Lexers when they are unable to tokenize the input string.
*/
public class TokenizeException extends AbacusException {
/**
* Create a new tokenize exception with no additional data.
*/
public TokenizeException() {
this("");
}
/**
* Create a new tokenize exception with the given message.
* @param message the message to use.
*/
public TokenizeException(String message){
super("Failed to tokenize string", message);
}
}

View File

@@ -1,9 +1,8 @@
package org.nwapw.abacus.lexing; package org.nwapw.abacus.lexing;
import org.nwapw.abacus.lexing.pattern.EndNode;
import org.nwapw.abacus.lexing.pattern.Match;
import org.nwapw.abacus.lexing.pattern.Pattern; import org.nwapw.abacus.lexing.pattern.Pattern;
import org.nwapw.abacus.lexing.pattern.PatternNode; import org.nwapw.abacus.lexing.pattern.nodes.EndNode;
import org.nwapw.abacus.lexing.pattern.nodes.PatternNode;
import java.util.*; import java.util.*;
@@ -80,10 +79,10 @@ public class Lexer<T> {
index++; index++;
} }
matches.sort((a, b) -> compare.compare(a.getType(), b.getType()));
if (compare != null) { if (compare != null) {
matches.sort(Comparator.comparingInt(a -> a.getContent().length())); Collections.sort(matches, (a, b) -> compare.compare(a.getType(), b.getType()));
} }
Collections.sort(matches, (o1, o2) -> o1.getContent().length() - o2.getContent().length());
return matches.isEmpty() ? null : matches.get(matches.size() - 1); return matches.isEmpty() ? null : matches.get(matches.size() - 1);
} }
@@ -137,7 +136,10 @@ public class Lexer<T> {
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(name, id); return Arrays.hashCode(new Object[] {
this.name,
this.id
});
} }
@Override @Override

View File

@@ -1,4 +1,4 @@
package org.nwapw.abacus.lexing.pattern; package org.nwapw.abacus.lexing;
/** /**
* A match that has been generated by the lexer. * A match that has been generated by the lexer.

View File

@@ -1,10 +1,11 @@
package org.nwapw.abacus.lexing.pattern; package org.nwapw.abacus.lexing.pattern;
import org.nwapw.abacus.lexing.pattern.nodes.*;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Stack; import java.util.Stack;
import java.util.function.Function;
/** /**
* A pattern that can be compiled from a string and used in lexing. * A pattern that can be compiled from a string and used in lexing.
@@ -34,8 +35,8 @@ public class Pattern<T> {
* A map of regex operator to functions that modify a PatternChain * A map of regex operator to functions that modify a PatternChain
* with the appropriate operation. * with the appropriate operation.
*/ */
private Map<Character, Function<PatternChain<T>, PatternChain<T>>> operations = private Map<Character, Transformation<T>> operations =
new HashMap<Character, Function<PatternChain<T>, PatternChain<T>>>() {{ new HashMap<Character, Transformation<T>>() {{
put('+', Pattern.this::transformPlus); put('+', Pattern.this::transformPlus);
put('*', Pattern.this::transformStar); put('*', Pattern.this::transformStar);
put('?', Pattern.this::transformQuestion); put('?', Pattern.this::transformQuestion);
@@ -88,7 +89,7 @@ public class Pattern<T> {
* @return the modified chain. * @return the modified chain.
*/ */
private PatternChain<T> transformPlus(PatternChain<T> chain) { private PatternChain<T> transformPlus(PatternChain<T> chain) {
chain.tail.outputStates.add(chain.head); chain.tail.getOutputStates().add(chain.head);
return chain; return chain;
} }
@@ -102,10 +103,10 @@ public class Pattern<T> {
private PatternChain<T> transformStar(PatternChain<T> chain) { private PatternChain<T> transformStar(PatternChain<T> chain) {
LinkNode<T> newTail = new LinkNode<>(); LinkNode<T> newTail = new LinkNode<>();
LinkNode<T> newHead = new LinkNode<>(); LinkNode<T> newHead = new LinkNode<>();
newHead.outputStates.add(chain.head); newHead.getOutputStates().add(chain.head);
newHead.outputStates.add(newTail); newHead.getOutputStates().add(newTail);
chain.tail.outputStates.add(newTail); chain.tail.getOutputStates().add(newTail);
newTail.outputStates.add(newHead); newTail.getOutputStates().add(newHead);
chain.head = newHead; chain.head = newHead;
chain.tail = newTail; chain.tail = newTail;
return chain; return chain;
@@ -121,9 +122,9 @@ public class Pattern<T> {
private PatternChain<T> transformQuestion(PatternChain<T> chain) { private PatternChain<T> transformQuestion(PatternChain<T> chain) {
LinkNode<T> newTail = new LinkNode<>(); LinkNode<T> newTail = new LinkNode<>();
LinkNode<T> newHead = new LinkNode<>(); LinkNode<T> newHead = new LinkNode<>();
newHead.outputStates.add(chain.head); newHead.getOutputStates().add(chain.head);
newHead.outputStates.add(newTail); newHead.getOutputStates().add(newTail);
chain.tail.outputStates.add(newTail); chain.tail.getOutputStates().add(newTail);
chain.head = newHead; chain.head = newHead;
chain.tail = newTail; chain.tail = newTail;
return chain; return chain;
@@ -140,8 +141,8 @@ public class Pattern<T> {
LinkNode<T> tail = new LinkNode<>(); LinkNode<T> tail = new LinkNode<>();
PatternChain<T> newChain = new PatternChain<>(head, tail); PatternChain<T> newChain = new PatternChain<>(head, tail);
for (PatternChain<T> chain : collection) { for (PatternChain<T> chain : collection) {
head.outputStates.add(chain.head); head.getOutputStates().add(chain.head);
chain.tail.outputStates.add(tail); chain.tail.getOutputStates().add(tail);
} }
return newChain; return newChain;
} }
@@ -205,7 +206,7 @@ public class Pattern<T> {
if (operations.containsKey(currentChar)) { if (operations.containsKey(currentChar)) {
if (currentChain == null) return null; if (currentChain == null) return null;
currentChain = operations.get(currentChar).apply(currentChain); currentChain = operations.get(currentChar).transform(currentChain);
fullChain.append(currentChain); fullChain.append(currentChain);
currentChain = null; currentChain = null;
index++; index++;

View File

@@ -1,5 +1,7 @@
package org.nwapw.abacus.lexing.pattern; package org.nwapw.abacus.lexing.pattern;
import org.nwapw.abacus.lexing.pattern.nodes.PatternNode;
/** /**
* A chain of nodes that can be treated as a single unit. * A chain of nodes that can be treated as a single unit.
* Used during pattern compilation. * Used during pattern compilation.
@@ -56,7 +58,7 @@ public class PatternChain<T> {
this.head = other.head; this.head = other.head;
this.tail = other.tail; this.tail = other.tail;
} else { } else {
tail.outputStates.add(other.head); tail.getOutputStates().add(other.head);
tail = other.tail; tail = other.tail;
} }
} }
@@ -72,7 +74,7 @@ public class PatternChain<T> {
if (tail == null) { if (tail == null) {
head = tail = node; head = tail = node;
} else { } else {
tail.outputStates.add(node); tail.getOutputStates().add(node);
tail = node; tail = node;
} }
} }

View File

@@ -0,0 +1,16 @@
package org.nwapw.abacus.lexing.pattern;
/**
* An interface that transforms a pattern chain into a different pattern chain.
* @param <T> the type used to identify the nodes in the pattern chain.
*/
public interface Transformation<T> {
/**
* Performs the actual transformation.
* @param from the original chain.
* @return the resulting chain.
*/
PatternChain<T> transform(PatternChain<T> from);
}

View File

@@ -1,4 +1,4 @@
package org.nwapw.abacus.lexing.pattern; package org.nwapw.abacus.lexing.pattern.nodes;
/** /**
* A pattern node that matches any character. * A pattern node that matches any character.

View File

@@ -1,4 +1,4 @@
package org.nwapw.abacus.lexing.pattern; package org.nwapw.abacus.lexing.pattern.nodes;
/** /**
* A node that represents a successful match. * A node that represents a successful match.

View File

@@ -1,4 +1,4 @@
package org.nwapw.abacus.lexing.pattern; package org.nwapw.abacus.lexing.pattern.nodes;
import java.util.Collection; import java.util.Collection;

View File

@@ -1,4 +1,4 @@
package org.nwapw.abacus.lexing.pattern; package org.nwapw.abacus.lexing.pattern.nodes;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
@@ -65,4 +65,11 @@ public class PatternNode<T> {
outputStates.forEach(e -> e.addInto(into)); outputStates.forEach(e -> e.addInto(into));
} }
/**
* Gets the output states of this node.
* @return the output states.
*/
public Set<PatternNode<T>> getOutputStates() {
return outputStates;
}
} }

View File

@@ -1,4 +1,4 @@
package org.nwapw.abacus.lexing.pattern; package org.nwapw.abacus.lexing.pattern.nodes;
/** /**
* A node that matches a range of characters. * A node that matches a range of characters.

View File

@@ -1,4 +1,4 @@
package org.nwapw.abacus.lexing.pattern; package org.nwapw.abacus.lexing.pattern.nodes;
/** /**
* A node that matches a single value. * A node that matches a single value.

View File

@@ -1,4 +1,6 @@
package org.nwapw.abacus.number; package org.nwapw.abacus.number.standard;
import org.nwapw.abacus.number.NumberInterface;
/** /**
* An implementation of NumberInterface using a double. * An implementation of NumberInterface using a double.

View File

@@ -1,4 +1,6 @@
package org.nwapw.abacus.number; package org.nwapw.abacus.number.standard;
import org.nwapw.abacus.number.NumberInterface;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.MathContext; import java.math.MathContext;

View File

@@ -1,20 +0,0 @@
package org.nwapw.abacus.parsing;
import java.util.List;
/**
* Interface that provides the ability to convert a string into a list of tokens.
*
* @param <T> the type of the tokens produced.
*/
public interface Tokenizer<T> {
/**
* Converts a string into tokens.
*
* @param string the string to convert.
* @return the list of tokens, or null on error.
*/
public List<T> tokenizeString(String string);
}

View File

@@ -1,50 +0,0 @@
package org.nwapw.abacus.parsing;
import org.nwapw.abacus.tree.TreeNode;
import java.util.List;
/**
* TreeBuilder class used to piece together a Tokenizer and
* Parser of the same kind. This is used to essentially avoid
* 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 <T> the type of tokens created by the tokenizer and used by the parser.
*/
public class TreeBuilder<T> {
/**
* The tokenizer used to convert a string into tokens.
*/
private Tokenizer<T> tokenizer;
/**
* The parser used to parse a list of tokens into a tree.
*/
private Parser<T> parser;
/**
* 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
*/
public TreeBuilder(Tokenizer<T> tokenizer, Parser<T> 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) {
List<T> tokens = tokenizer.tokenizeString(input);
if (tokens == null) return null;
return parser.constructTree(tokens);
}
}

View File

@@ -1,11 +1,12 @@
package org.nwapw.abacus.parsing; package org.nwapw.abacus.parsing.standard;
import org.nwapw.abacus.exception.TokenizeException;
import org.nwapw.abacus.lexing.Lexer; import org.nwapw.abacus.lexing.Lexer;
import org.nwapw.abacus.lexing.pattern.Match; import org.nwapw.abacus.lexing.Match;
import org.nwapw.abacus.lexing.pattern.Pattern; import org.nwapw.abacus.lexing.pattern.Pattern;
import org.nwapw.abacus.parsing.Tokenizer;
import org.nwapw.abacus.plugin.PluginListener; import org.nwapw.abacus.plugin.PluginListener;
import org.nwapw.abacus.plugin.PluginManager; import org.nwapw.abacus.plugin.PluginManager;
import org.nwapw.abacus.tree.TokenType;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
@@ -19,7 +20,7 @@ public class LexerTokenizer implements Tokenizer<Match<TokenType>>, PluginListen
/** /**
* Comparator used to sort the tokens produced by the lexer. * Comparator used to sort the tokens produced by the lexer.
*/ */
protected static final Comparator<TokenType> TOKEN_SORTER = Comparator.comparingInt(e -> e.priority); protected static final Comparator<TokenType> TOKEN_SORTER = (o1, o2) -> o1.priority - o2.priority;
/** /**
* The lexer instance used to turn strings into matches. * The lexer instance used to turn strings into matches.
@@ -42,7 +43,9 @@ public class LexerTokenizer implements Tokenizer<Match<TokenType>>, PluginListen
@Override @Override
public List<Match<TokenType>> tokenizeString(String string) { public List<Match<TokenType>> tokenizeString(String string) {
return lexer.lexAll(string, 0, TOKEN_SORTER); List<Match<TokenType>> tokens = lexer.lexAll(string, 0, TOKEN_SORTER);
if(tokens == null) throw new TokenizeException();
return tokens;
} }
@Override @Override

View File

@@ -1,12 +1,14 @@
package org.nwapw.abacus.parsing; package org.nwapw.abacus.parsing.standard;
import org.nwapw.abacus.exception.ParseException;
import org.nwapw.abacus.function.Operator; import org.nwapw.abacus.function.Operator;
import org.nwapw.abacus.function.OperatorAssociativity; import org.nwapw.abacus.function.OperatorAssociativity;
import org.nwapw.abacus.function.OperatorType; import org.nwapw.abacus.function.OperatorType;
import org.nwapw.abacus.lexing.pattern.Match; import org.nwapw.abacus.lexing.Match;
import org.nwapw.abacus.parsing.Parser;
import org.nwapw.abacus.plugin.PluginListener; import org.nwapw.abacus.plugin.PluginListener;
import org.nwapw.abacus.plugin.PluginManager; import org.nwapw.abacus.plugin.PluginManager;
import org.nwapw.abacus.tree.*; import org.nwapw.abacus.tree.nodes.*;
import java.util.*; import java.util.*;
@@ -99,7 +101,7 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
while (!tokenStack.empty() && tokenStack.peek().getType() != TokenType.OPEN_PARENTH) { while (!tokenStack.empty() && tokenStack.peek().getType() != TokenType.OPEN_PARENTH) {
output.add(tokenStack.pop()); output.add(tokenStack.pop());
} }
if (tokenStack.empty()) return null; if (tokenStack.empty()) throw new ParseException("mismatched parentheses");
if (matchType == TokenType.CLOSE_PARENTH) { if (matchType == TokenType.CLOSE_PARENTH) {
tokenStack.pop(); tokenStack.pop();
} }
@@ -111,7 +113,7 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
if (!(newMatchType == TokenType.OP || if (!(newMatchType == TokenType.OP ||
newMatchType == TokenType.TREE_VALUE_OP || newMatchType == TokenType.TREE_VALUE_OP ||
newMatchType == TokenType.FUNCTION || newMatchType == TokenType.FUNCTION ||
newMatchType == TokenType.TREE_VALUE_FUNCTION)) return null; newMatchType == TokenType.TREE_VALUE_FUNCTION)) throw new ParseException("mismatched parentheses");
output.add(tokenStack.pop()); output.add(tokenStack.pop());
} }
return output; return output;
@@ -123,8 +125,8 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
* @param matches the list of tokens from the source string. * @param matches the list of tokens from the source string.
* @return the construct tree expression. * @return the construct tree expression.
*/ */
public TreeNode constructRecursive(List<Match<TokenType>> matches) { public TreeNode constructRecursive(List<? extends Match<TokenType>> matches) {
if (matches.size() == 0) return null; if (matches.size() == 0) throw new ParseException("no tokens left in input");
Match<TokenType> match = matches.remove(0); Match<TokenType> match = matches.remove(0);
TokenType matchType = match.getType(); TokenType matchType = match.getType();
if (matchType == TokenType.OP || matchType == TokenType.TREE_VALUE_OP) { if (matchType == TokenType.OP || matchType == TokenType.TREE_VALUE_OP) {
@@ -133,7 +135,6 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
if (type == OperatorType.BINARY_INFIX) { if (type == OperatorType.BINARY_INFIX) {
TreeNode right = constructRecursive(matches); TreeNode right = constructRecursive(matches);
TreeNode left = constructRecursive(matches); TreeNode left = constructRecursive(matches);
if (left == null || right == null) return null;
if (matchType == TokenType.OP) { if (matchType == TokenType.OP) {
return new NumberBinaryNode(operator, left, right); return new NumberBinaryNode(operator, left, right);
} else { } else {
@@ -141,7 +142,6 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
} }
} else { } else {
TreeNode applyTo = constructRecursive(matches); TreeNode applyTo = constructRecursive(matches);
if (applyTo == null) return null;
if (matchType == TokenType.OP) { if (matchType == TokenType.OP) {
return new NumberUnaryNode(operator, applyTo); return new NumberUnaryNode(operator, applyTo);
} else { } else {
@@ -157,29 +157,29 @@ public class ShuntingYardParser implements Parser<Match<TokenType>>, PluginListe
List<TreeNode> children = new ArrayList<>(); List<TreeNode> children = new ArrayList<>();
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); TreeNode argument = constructRecursive(matches);
if (argument == null) return null;
children.add(0, argument); children.add(0, argument);
} }
if (matches.isEmpty()) return null; if (matches.isEmpty()) throw new ParseException("incorrectly formatted function call");
matches.remove(0); matches.remove(0);
CallNode node; CallNode node;
if (matchType == TokenType.FUNCTION) { if (matchType == TokenType.FUNCTION) {
node = new FunctionNode(functionName, children); node = new NumberFunctionNode(functionName, children);
} else { } else {
node = new TreeValueFunctionNode(functionName, children); node = new TreeValueFunctionNode(functionName, children);
} }
return node; return node;
} }
return null; throw new ParseException("unrecognized token");
} }
@Override @Override
public TreeNode constructTree(List<Match<TokenType>> tokens) { public TreeNode constructTree(List<? extends Match<TokenType>> tokens) {
if (tokens.isEmpty()) throw new ParseException("no input tokens");
tokens = intoPostfix(new ArrayList<>(tokens)); tokens = intoPostfix(new ArrayList<>(tokens));
if (tokens == null) return null;
Collections.reverse(tokens); Collections.reverse(tokens);
TreeNode constructedTree = constructRecursive(tokens); TreeNode constructedTree = constructRecursive(tokens);
return tokens.size() == 0 ? constructedTree : null; if(tokens.size() == 0) return constructedTree;
throw new ParseException("could not parse all input");
} }
@Override @Override

View File

@@ -1,4 +1,4 @@
package org.nwapw.abacus.tree; package org.nwapw.abacus.parsing.standard;
/** /**
* Enum to represent the type of the token that has been matched * Enum to represent the type of the token that has been matched

View File

@@ -1,10 +1,10 @@
package org.nwapw.abacus.plugin; package org.nwapw.abacus.plugin;
import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.number.NumberInterface;
import org.nwapw.abacus.number.promotion.PromotionFunction;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Function;
/** /**
* A class that holds data about a number implementation. * A class that holds data about a number implementation.
@@ -14,7 +14,7 @@ public abstract class NumberImplementation {
/** /**
* The list of paths through which this implementation can be promoted. * The list of paths through which this implementation can be promoted.
*/ */
private Map<String, Function<NumberInterface, NumberInterface>> promotionPaths; private Map<String, PromotionFunction> promotionPaths;
/** /**
* The implementation class for this implementation. * The implementation class for this implementation.
*/ */
@@ -41,7 +41,7 @@ public abstract class NumberImplementation {
* *
* @return the map of documentation paths. * @return the map of documentation paths.
*/ */
public final Map<String, Function<NumberInterface, NumberInterface>> getPromotionPaths() { public final Map<String, PromotionFunction> getPromotionPaths() {
return promotionPaths; return promotionPaths;
} }

View File

@@ -1,6 +1,11 @@
package org.nwapw.abacus.plugin; package org.nwapw.abacus.plugin;
import org.nwapw.abacus.function.*; import org.nwapw.abacus.function.Documentation;
import org.nwapw.abacus.function.DocumentationType;
import org.nwapw.abacus.function.interfaces.NumberFunction;
import org.nwapw.abacus.function.interfaces.NumberOperator;
import org.nwapw.abacus.function.interfaces.TreeValueFunction;
import org.nwapw.abacus.function.interfaces.TreeValueOperator;
import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.number.NumberInterface;
/** /**

View File

@@ -1,7 +1,12 @@
package org.nwapw.abacus.plugin; package org.nwapw.abacus.plugin;
import org.nwapw.abacus.Abacus; import org.nwapw.abacus.Abacus;
import org.nwapw.abacus.function.*; import org.nwapw.abacus.function.Documentation;
import org.nwapw.abacus.function.DocumentationType;
import org.nwapw.abacus.function.interfaces.NumberFunction;
import org.nwapw.abacus.function.interfaces.NumberOperator;
import org.nwapw.abacus.function.interfaces.TreeValueFunction;
import org.nwapw.abacus.function.interfaces.TreeValueOperator;
import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.number.NumberInterface;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@@ -141,6 +146,7 @@ public class PluginManager {
registeredNumberImplementations.put(name, implementation); registeredNumberImplementations.put(name, implementation);
interfaceImplementationNames.put(implementation.getImplementation(), name); interfaceImplementationNames.put(implementation.getImplementation(), name);
interfaceImplementations.put(implementation.getImplementation(), implementation); interfaceImplementations.put(implementation.getImplementation(), implementation);
cachedPi.put(implementation.getImplementation(), implementation.instanceForPi());
} }
/** /**
@@ -218,10 +224,6 @@ public class PluginManager {
break; break;
} }
} }
if (toReturn == null) {
toReturn = new Documentation(name, "", "", "", type);
registerDocumentation(toReturn);
}
return toReturn; return toReturn;
} }
@@ -252,14 +254,7 @@ public class PluginManager {
* @return pi * @return pi
*/ */
public NumberInterface piFor(Class<? extends NumberInterface> forClass) { public NumberInterface piFor(Class<? extends NumberInterface> forClass) {
if (cachedPi.containsKey(forClass)) return cachedPi.get(forClass); return cachedPi.get(forClass);
NumberImplementation implementation = interfaceImplementationFor(forClass);
NumberInterface generatedPi = null;
if (implementation != null) {
generatedPi = implementation.instanceForPi();
}
cachedPi.put(forClass, generatedPi);
return generatedPi;
} }
/** /**

View File

@@ -1,12 +1,20 @@
package org.nwapw.abacus.plugin; package org.nwapw.abacus.plugin.standard;
import org.jetbrains.annotations.NotNull;
import org.nwapw.abacus.context.MutableEvaluationContext; import org.nwapw.abacus.context.MutableEvaluationContext;
import org.nwapw.abacus.function.*; import org.nwapw.abacus.context.PluginEvaluationContext;
import org.nwapw.abacus.number.NaiveNumber; import org.nwapw.abacus.function.Documentation;
import org.nwapw.abacus.function.DocumentationType;
import org.nwapw.abacus.function.interfaces.NumberFunction;
import org.nwapw.abacus.function.interfaces.NumberOperator;
import org.nwapw.abacus.function.interfaces.TreeValueOperator;
import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.number.NumberInterface;
import org.nwapw.abacus.number.PreciseNumber; import org.nwapw.abacus.number.standard.NaiveNumber;
import org.nwapw.abacus.tree.TreeNode; import org.nwapw.abacus.number.standard.PreciseNumber;
import org.nwapw.abacus.tree.VariableNode; import org.nwapw.abacus.plugin.NumberImplementation;
import org.nwapw.abacus.plugin.Plugin;
import org.nwapw.abacus.plugin.PluginManager;
import org.nwapw.abacus.plugin.standard.operator.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@@ -20,93 +28,27 @@ public class StandardPlugin extends Plugin {
/** /**
* The set operator. * The set operator.
*/ */
public final TreeValueOperator opSet = new TreeValueOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) { public static final TreeValueOperator OP_SET = new OperatorSet();
@Override
public boolean matchesParams(MutableEvaluationContext context, TreeNode[] params) {
return params.length == 2 && params[0] instanceof VariableNode;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, TreeNode[] params) {
String assignTo = ((VariableNode) params[0]).getVariable();
NumberInterface value = params[1].reduce(context.getInheritedReducer());
context.setVariable(assignTo, value);
return value;
}
};
/** /**
* The define operator. * The define operator.
*/ */
public final TreeValueOperator opDefine = new TreeValueOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) { public final TreeValueOperator OP_DEFINE = new OperatorDefine();
@Override
public boolean matchesParams(MutableEvaluationContext context, TreeNode[] params) {
return params.length == 2 && params[0] instanceof VariableNode;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, TreeNode[] params) {
String assignTo = ((VariableNode) params[0]).getVariable();
context.setDefinition(assignTo, params[1]);
return params[1].reduce(context.getInheritedReducer());
}
};
/** /**
* The addition operator, + * The addition operator, +
*/ */
public static final NumberOperator OP_ADD = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) { public static final NumberOperator OP_ADD = new OperatorAdd();
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
return params.length == 2;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
return params[0].add(params[1]);
}
};
/** /**
* The subtraction operator, - * The subtraction operator, -
*/ */
public static final NumberOperator OP_SUBTRACT = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) { public static final NumberOperator OP_SUBTRACT = new OperatorSubtract();
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
return params.length == 2;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
return params[0].subtract(params[1]);
}
};
/** /**
* The negation operator, - * The negation operator, -
*/ */
public static final NumberOperator OP_NEGATE = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.UNARY_PREFIX, 0) { public static final NumberOperator OP_NEGATE = new OperatorNegate();
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
return params.length == 1;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
return params[0].negate();
}
};
/** /**
* The multiplication operator, * * The multiplication operator, *
*/ */
public static final NumberOperator OP_MULTIPLY = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1) { public static final NumberOperator OP_MULTIPLY = new OperatorMultiply();
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
return params.length == 2;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
return params[0].multiply(params[1]);
}
};
/** /**
* The implementation for double-based naive numbers. * The implementation for double-based naive numbers.
*/ */
@@ -161,107 +103,30 @@ public class StandardPlugin extends Plugin {
/** /**
* The division operator, / * The division operator, /
*/ */
public static final NumberOperator OP_DIVIDE = new NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1) { public static final NumberOperator OP_DIVIDE = new OperatorDivide();
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
return params.length == 2 && params[1].compareTo(context.getInheritedNumberImplementation().instanceForString(Integer.toString(0))) != 0;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
return params[0].divide(params[1]);
}
};
/** /**
* The factorial operator, ! * The factorial operator, !
*/ */
public static final NumberOperator OP_FACTORIAL = new NumberOperator(OperatorAssociativity.RIGHT, OperatorType.UNARY_POSTFIX, 0) { public static final NumberOperator OP_FACTORIAL = new OperatorFactorial();
//private HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>> storedList = new HashMap<Class<? extends NumberInterface>, ArrayList<NumberInterface>>();
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
return params.length == 1
&& params[0].fractionalPart().compareTo(context.getInheritedNumberImplementation().instanceForString("0")) == 0
&& params[0].signum() >= 0;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
NumberImplementation implementation = context.getInheritedNumberImplementation();
if (params[0].signum() == 0) {
return implementation.instanceForString("1");
}
NumberInterface one = implementation.instanceForString("1");
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(one)).signum() == 1) {
factorial = factorial.multiply(multiplier);
}
return factorial;
/*if(!storedList.containsKey(params[0].getClass())){
storedList.put(params[0].getClass(), new ArrayList<NumberInterface>());
storedList.get(params[0].getClass()).add(NaiveNumber.ONE.promoteTo(params[0].getClass()));
storedList.get(params[0].getClass()).add(NaiveNumber.ONE.promoteTo(params[0].getClass()));
}*/
}
};
/** /**
* The permutation operator. * The permutation operator.
*/ */
public static final NumberOperator OP_NPR = new NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 0) { public static final NumberOperator OP_NPR = new OperatorNpr();
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
return params.length == 2 && params[0].fractionalPart().signum() == 0
&& params[1].fractionalPart().signum() == 0;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
NumberImplementation implementation = context.getInheritedNumberImplementation();
if (params[0].compareTo(params[1]) < 0 ||
params[0].signum() < 0 ||
(params[0].signum() == 0 && params[1].signum() != 0)) return implementation.instanceForString("0");
NumberInterface total = implementation.instanceForString("1");
NumberInterface multiplyBy = params[0];
NumberInterface remainingMultiplications = params[1];
NumberInterface halfway = params[0].divide(implementation.instanceForString("2"));
if (remainingMultiplications.compareTo(halfway) > 0) {
remainingMultiplications = params[0].subtract(remainingMultiplications);
}
while (remainingMultiplications.signum() > 0) {
total = total.multiply(multiplyBy);
remainingMultiplications = remainingMultiplications.subtract(implementation.instanceForString("1"));
multiplyBy = multiplyBy.subtract(implementation.instanceForString("1"));
}
return total;
}
};
/** /**
* The combination operator. * The combination operator.
*/ */
public static final NumberOperator OP_NCR = new NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 0) { public static final NumberOperator OP_NCR = new OperatorNcr();
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
return params.length == 2 && params[0].fractionalPart().signum() == 0
&& params[1].fractionalPart().signum() == 0;
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
return OP_NPR.apply(context, params).divide(OP_FACTORIAL.apply(context, params[1]));
}
};
/** /**
* The absolute value function, abs(-3) = 3 * The absolute value function, abs(-3) = 3
*/ */
public static final NumberFunction FUNCTION_ABS = new NumberFunction() { public static final NumberFunction FUNCTION_ABS = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 1; return params.length == 1;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
return params[0].multiply(context.getInheritedNumberImplementation().instanceForString(Integer.toString(params[0].signum()))); return params[0].multiply(context.getInheritedNumberImplementation().instanceForString(Integer.toString(params[0].signum())));
} }
}; };
@@ -270,12 +135,12 @@ public class StandardPlugin extends Plugin {
*/ */
public static final NumberFunction FUNCTION_LN = new NumberFunction() { public static final NumberFunction FUNCTION_LN = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 1 && params[0].compareTo(context.getInheritedNumberImplementation().instanceForString("0")) > 0; return params.length == 1 && params[0].compareTo(context.getInheritedNumberImplementation().instanceForString("0")) > 0;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
NumberImplementation implementation = context.getInheritedNumberImplementation(); NumberImplementation implementation = context.getInheritedNumberImplementation();
NumberInterface param = params[0]; NumberInterface param = params[0];
NumberInterface one = implementation.instanceForString("1"); NumberInterface one = implementation.instanceForString("1");
@@ -303,10 +168,12 @@ public class StandardPlugin extends Plugin {
/** /**
* Returns the partial sum of the Taylor series for logx (around x=1). * Returns the partial sum of the Taylor series for logx (around x=1).
* Automatically determines the number of terms needed based on the precision of x. * Automatically determines the number of terms needed based on the precision of x.
*
* @param context
* @param x value at which the series is evaluated. 0 < x < 2. (x=2 is convergent but impractical.) * @param x value at which the series is evaluated. 0 < x < 2. (x=2 is convergent but impractical.)
* @return the partial sum. * @return the partial sum.
*/ */
private NumberInterface getLogPartialSum(MutableEvaluationContext context, NumberInterface x) { private NumberInterface getLogPartialSum(PluginEvaluationContext context, NumberInterface x) {
NumberImplementation implementation = context.getInheritedNumberImplementation(); NumberImplementation implementation = context.getInheritedNumberImplementation();
NumberInterface maxError = x.getMaxError(); NumberInterface maxError = x.getMaxError();
x = x.subtract(implementation.instanceForString("1")); //Terms used are for log(x+1). x = x.subtract(implementation.instanceForString("1")); //Terms used are for log(x+1).
@@ -332,7 +199,7 @@ public class StandardPlugin extends Plugin {
//We'll use the series \sigma_{n >= 1) ((1/3^n + 1/4^n) * 1/n) //We'll use the series \sigma_{n >= 1) ((1/3^n + 1/4^n) * 1/n)
//In the following, a=1/3^n, b=1/4^n, c = 1/n. //In the following, a=1/3^n, b=1/4^n, c = 1/n.
//a is also an error bound. //a is also an error bound.
NumberInterface a = implementation.instanceForString("1"), b = a, c = a; NumberInterface a = implementation.instanceForString("1"), b = a, c;
NumberInterface sum = implementation.instanceForString("0"); NumberInterface sum = implementation.instanceForString("0");
NumberInterface one = implementation.instanceForString("1"); NumberInterface one = implementation.instanceForString("1");
int n = 0; int n = 0;
@@ -351,57 +218,30 @@ public class StandardPlugin extends Plugin {
*/ */
public static final NumberFunction FUNCTION_RAND_INT = new NumberFunction() { public static final NumberFunction FUNCTION_RAND_INT = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 1; return params.length == 1;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
return context.getInheritedNumberImplementation().instanceForString(Long.toString(Math.round(Math.random() * params[0].floor().intValue()))); return context.getInheritedNumberImplementation().instanceForString(Long.toString(Math.round(Math.random() * params[0].floor().intValue())));
} }
}; };
/** /**
* The caret / pow operator, ^ * The caret / pow operator, ^
*/ */
public static final NumberOperator OP_CARET = new NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 2) { public static final NumberOperator OP_CARET = new OperatorCaret();
@Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) {
NumberInterface zero = context.getInheritedNumberImplementation().instanceForString("0");
return params.length == 2
&& !(params[0].compareTo(zero) == 0
&& params[1].compareTo(zero) == 0)
&& !(params[0].signum() == -1 && params[1].fractionalPart().compareTo(zero) != 0);
}
@Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) {
NumberImplementation implementation = context.getInheritedNumberImplementation();
NumberInterface zero = implementation.instanceForString("0");
if (params[0].compareTo(zero) == 0)
return zero;
else if (params[1].compareTo(zero) == 0)
return implementation.instanceForString("1");
//Detect integer bases:
if (params[0].fractionalPart().compareTo(implementation.instanceForString("0")) == 0
&& FUNCTION_ABS.apply(context, params[1]).compareTo(implementation.instanceForString(Integer.toString(Integer.MAX_VALUE))) < 0
&& FUNCTION_ABS.apply(context, params[1]).compareTo(implementation.instanceForString("1")) >= 0) {
NumberInterface[] newParams = {params[0], params[1].fractionalPart()};
return params[0].intPow(params[1].floor().intValue()).multiply(applyInternal(context, newParams));
}
return FUNCTION_EXP.apply(context, FUNCTION_LN.apply(context, FUNCTION_ABS.apply(context, params[0])).multiply(params[1]));
}
};
/** /**
* The square root function. * The square root function.
*/ */
public static final NumberFunction FUNCTION_SQRT = new NumberFunction() { public static final NumberFunction FUNCTION_SQRT = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 1; return params.length == 1;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
return OP_CARET.apply(context, params[0], context.getInheritedNumberImplementation().instanceForString(".5")); return OP_CARET.apply(context, params[0], context.getInheritedNumberImplementation().instanceForString(".5"));
} }
}; };
@@ -411,12 +251,12 @@ public class StandardPlugin extends Plugin {
*/ */
public static final NumberFunction FUNCTION_EXP = new NumberFunction() { public static final NumberFunction FUNCTION_EXP = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 1; return params.length == 1;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
NumberImplementation implementation = context.getInheritedNumberImplementation(); NumberImplementation implementation = context.getInheritedNumberImplementation();
NumberInterface maxError = params[0].getMaxError(); NumberInterface maxError = params[0].getMaxError();
int n = 0; int n = 0;
@@ -449,12 +289,12 @@ public class StandardPlugin extends Plugin {
*/ */
public final NumberFunction functionSin = new NumberFunction() { public final NumberFunction functionSin = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 1; return params.length == 1;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
NumberImplementation implementation = context.getInheritedNumberImplementation(); NumberImplementation implementation = context.getInheritedNumberImplementation();
NumberInterface pi = piFor(params[0].getClass()); NumberInterface pi = piFor(params[0].getClass());
NumberInterface twoPi = pi.multiply(implementation.instanceForString("2")); NumberInterface twoPi = pi.multiply(implementation.instanceForString("2"));
@@ -474,12 +314,12 @@ public class StandardPlugin extends Plugin {
*/ */
public final NumberFunction functionCos = new NumberFunction() { public final NumberFunction functionCos = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 1; return params.length == 1;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
return functionSin.apply(context, piFor(params[0].getClass()).divide(context.getInheritedNumberImplementation().instanceForString("2")) return functionSin.apply(context, piFor(params[0].getClass()).divide(context.getInheritedNumberImplementation().instanceForString("2"))
.subtract(params[0])); .subtract(params[0]));
} }
@@ -489,12 +329,12 @@ public class StandardPlugin extends Plugin {
*/ */
public final NumberFunction functionTan = new NumberFunction() { public final NumberFunction functionTan = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 1; return params.length == 1;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
return functionSin.apply(context, params[0]).divide(functionCos.apply(context, params[0])); return functionSin.apply(context, params[0]).divide(functionCos.apply(context, params[0]));
} }
}; };
@@ -503,12 +343,12 @@ public class StandardPlugin extends Plugin {
*/ */
public final NumberFunction functionSec = new NumberFunction() { public final NumberFunction functionSec = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 1; return params.length == 1;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
return context.getInheritedNumberImplementation().instanceForString("1").divide(functionCos.apply(context, params[0])); return context.getInheritedNumberImplementation().instanceForString("1").divide(functionCos.apply(context, params[0]));
} }
}; };
@@ -517,12 +357,12 @@ public class StandardPlugin extends Plugin {
*/ */
public final NumberFunction functionCsc = new NumberFunction() { public final NumberFunction functionCsc = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 1; return params.length == 1;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
return context.getInheritedNumberImplementation().instanceForString("1").divide(functionSin.apply(context, params[0])); return context.getInheritedNumberImplementation().instanceForString("1").divide(functionSin.apply(context, params[0]));
} }
}; };
@@ -531,12 +371,12 @@ public class StandardPlugin extends Plugin {
*/ */
public final NumberFunction functionCot = new NumberFunction() { public final NumberFunction functionCot = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 1; return params.length == 1;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
return functionCos.apply(context, params[0]).divide(functionSin.apply(context, params[0])); return functionCos.apply(context, params[0]).divide(functionSin.apply(context, params[0]));
} }
}; };
@@ -546,13 +386,13 @@ public class StandardPlugin extends Plugin {
*/ */
public final NumberFunction functionArcsin = new NumberFunction() { public final NumberFunction functionArcsin = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 1 return params.length == 1
&& FUNCTION_ABS.apply(context, params[0]).compareTo(context.getInheritedNumberImplementation().instanceForString("1")) <= 0; && FUNCTION_ABS.apply(context, params[0]).compareTo(context.getInheritedNumberImplementation().instanceForString("1")) <= 0;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
NumberImplementation implementation = context.getInheritedNumberImplementation(); NumberImplementation implementation = context.getInheritedNumberImplementation();
if (FUNCTION_ABS.apply(context, params[0]).compareTo(implementation.instanceForString(".8")) >= 0) { if (FUNCTION_ABS.apply(context, params[0]).compareTo(implementation.instanceForString(".8")) >= 0) {
NumberInterface[] newParams = {FUNCTION_SQRT.apply(context, implementation.instanceForString("1").subtract(params[0].multiply(params[0])))}; NumberInterface[] newParams = {FUNCTION_SQRT.apply(context, implementation.instanceForString("1").subtract(params[0].multiply(params[0])))};
@@ -580,12 +420,12 @@ public class StandardPlugin extends Plugin {
*/ */
public final NumberFunction functionArccos = new NumberFunction() { public final NumberFunction functionArccos = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 1 && FUNCTION_ABS.apply(context, params[0]).compareTo(context.getInheritedNumberImplementation().instanceForString("1")) <= 0; return params.length == 1 && FUNCTION_ABS.apply(context, params[0]).compareTo(context.getInheritedNumberImplementation().instanceForString("1")) <= 0;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
return piFor(params[0].getClass()).divide(context.getInheritedNumberImplementation().instanceForString("2")) return piFor(params[0].getClass()).divide(context.getInheritedNumberImplementation().instanceForString("2"))
.subtract(functionArcsin.apply(context, params)); .subtract(functionArcsin.apply(context, params));
} }
@@ -596,12 +436,12 @@ public class StandardPlugin extends Plugin {
*/ */
public final NumberFunction functionArccsc = new NumberFunction() { public final NumberFunction functionArccsc = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 1 && FUNCTION_ABS.apply(context, params[0]).compareTo(context.getInheritedNumberImplementation().instanceForString("1")) >= 0; return params.length == 1 && FUNCTION_ABS.apply(context, params[0]).compareTo(context.getInheritedNumberImplementation().instanceForString("1")) >= 0;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
NumberInterface[] reciprocalParamArr = {context.getInheritedNumberImplementation().instanceForString("1").divide(params[0])}; NumberInterface[] reciprocalParamArr = {context.getInheritedNumberImplementation().instanceForString("1").divide(params[0])};
return functionArcsin.apply(context, reciprocalParamArr); return functionArcsin.apply(context, reciprocalParamArr);
} }
@@ -612,12 +452,12 @@ public class StandardPlugin extends Plugin {
*/ */
public final NumberFunction functionArcsec = new NumberFunction() { public final NumberFunction functionArcsec = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 1 && FUNCTION_ABS.apply(context, params[0]).compareTo(context.getInheritedNumberImplementation().instanceForString("1")) >= 0; return params.length == 1 && FUNCTION_ABS.apply(context, params[0]).compareTo(context.getInheritedNumberImplementation().instanceForString("1")) >= 0;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
NumberInterface[] reciprocalParamArr = {context.getInheritedNumberImplementation().instanceForString("1").divide(params[0])}; NumberInterface[] reciprocalParamArr = {context.getInheritedNumberImplementation().instanceForString("1").divide(params[0])};
return functionArccos.apply(context, reciprocalParamArr); return functionArccos.apply(context, reciprocalParamArr);
} }
@@ -628,12 +468,12 @@ public class StandardPlugin extends Plugin {
*/ */
public final NumberFunction functionArctan = new NumberFunction() { public final NumberFunction functionArctan = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 1; return params.length == 1;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
NumberImplementation implementation = context.getInheritedNumberImplementation(); NumberImplementation implementation = context.getInheritedNumberImplementation();
if (params[0].signum() == -1) { if (params[0].signum() == -1) {
NumberInterface[] negatedParams = {params[0].negate()}; NumberInterface[] negatedParams = {params[0].negate()};
@@ -670,12 +510,12 @@ public class StandardPlugin extends Plugin {
*/ */
public final NumberFunction functionArccot = new NumberFunction() { public final NumberFunction functionArccot = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 1; return params.length == 1;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
return piFor(params[0].getClass()).divide(context.getInheritedNumberImplementation().instanceForString("2")) return piFor(params[0].getClass()).divide(context.getInheritedNumberImplementation().instanceForString("2"))
.subtract(functionArctan.apply(context, params)); .subtract(functionArctan.apply(context, params));
} }
@@ -693,8 +533,7 @@ public class StandardPlugin extends Plugin {
* @param n non-negative integer. * @param n non-negative integer.
* @return a number of numClass with value n factorial. * @return a number of numClass with value n factorial.
*/ */
public static NumberInterface factorial(NumberImplementation implementation, int n) { synchronized public static NumberInterface factorial(NumberImplementation implementation, int n) {
if (!FACTORIAL_LISTS.containsKey(implementation)) { if (!FACTORIAL_LISTS.containsKey(implementation)) {
FACTORIAL_LISTS.put(implementation, new ArrayList<>()); FACTORIAL_LISTS.put(implementation, new ArrayList<>());
FACTORIAL_LISTS.get(implementation).add(implementation.instanceForString("1")); FACTORIAL_LISTS.get(implementation).add(implementation.instanceForString("1"));
@@ -715,8 +554,8 @@ public class StandardPlugin extends Plugin {
* @param x where the series is evaluated. * @param x where the series is evaluated.
* @return the value of the series * @return the value of the series
*/ */
private static NumberInterface sinTaylor(MutableEvaluationContext context, NumberInterface x) { private static NumberInterface sinTaylor(PluginEvaluationContext context, NumberInterface x) {
NumberInterface power = x, multiplier = x.multiply(x).negate(), currentTerm = x, sum = x; NumberInterface power = x, multiplier = x.multiply(x).negate(), currentTerm, sum = x;
NumberInterface maxError = x.getMaxError(); NumberInterface maxError = x.getMaxError();
int n = 1; int n = 1;
do { do {
@@ -734,7 +573,7 @@ public class StandardPlugin extends Plugin {
* @param phi an angle (in radians). * @param phi an angle (in radians).
* @return theta in [0, 2pi) that differs from phi by a multiple of 2pi. * @return theta in [0, 2pi) that differs from phi by a multiple of 2pi.
*/ */
private static NumberInterface getSmallAngle(MutableEvaluationContext context, NumberInterface phi, NumberInterface pi) { private static NumberInterface getSmallAngle(PluginEvaluationContext context, NumberInterface phi, NumberInterface pi) {
NumberInterface twoPi = pi.multiply(context.getInheritedNumberImplementation().instanceForString("2")); NumberInterface twoPi = pi.multiply(context.getInheritedNumberImplementation().instanceForString("2"));
NumberInterface theta = FUNCTION_ABS.apply(context, phi).subtract(twoPi NumberInterface theta = FUNCTION_ABS.apply(context, phi).subtract(twoPi
.multiply(FUNCTION_ABS.apply(context, phi).divide(twoPi).floor())); //Now theta is in [0, 2pi). .multiply(FUNCTION_ABS.apply(context, phi).divide(twoPi).floor())); //Now theta is in [0, 2pi).
@@ -757,8 +596,8 @@ public class StandardPlugin extends Plugin {
registerOperator("^", OP_CARET); registerOperator("^", OP_CARET);
registerOperator("!", OP_FACTORIAL); registerOperator("!", OP_FACTORIAL);
registerTreeValueOperator("=", opSet); registerTreeValueOperator("=", OP_SET);
registerTreeValueOperator(":=", opDefine); registerTreeValueOperator(":=", OP_DEFINE);
registerOperator("nPr", OP_NPR); registerOperator("nPr", OP_NPR);
registerOperator("nCr", OP_NCR); registerOperator("nCr", OP_NCR);

View File

@@ -1,17 +1,15 @@
package org.nwapw.abacus package org.nwapw.abacus
import org.nwapw.abacus.config.Configuration import org.nwapw.abacus.config.Configuration
import org.nwapw.abacus.context.MutableEvaluationContext
import org.nwapw.abacus.context.EvaluationContext import org.nwapw.abacus.context.EvaluationContext
import org.nwapw.abacus.number.PromotionManager import org.nwapw.abacus.context.MutableEvaluationContext
import org.nwapw.abacus.parsing.LexerTokenizer import org.nwapw.abacus.number.promotion.PromotionManager
import org.nwapw.abacus.parsing.ShuntingYardParser
import org.nwapw.abacus.parsing.TreeBuilder import org.nwapw.abacus.parsing.TreeBuilder
import org.nwapw.abacus.parsing.standard.LexerTokenizer
import org.nwapw.abacus.parsing.standard.ShuntingYardParser
import org.nwapw.abacus.plugin.PluginManager import org.nwapw.abacus.plugin.PluginManager
import org.nwapw.abacus.plugin.StandardPlugin import org.nwapw.abacus.plugin.standard.StandardPlugin
import org.nwapw.abacus.tree.EvaluationResult import org.nwapw.abacus.tree.nodes.TreeNode
import org.nwapw.abacus.tree.NumberReducer
import org.nwapw.abacus.tree.TreeNode
/** /**
* Core class to handle all mathematics. * Core class to handle all mathematics.
@@ -48,7 +46,7 @@ class Abacus(val configuration: Configuration) {
/** /**
* The hidden, mutable implementation of the context. * The hidden, mutable implementation of the context.
*/ */
private val mutableContext = MutableEvaluationContext(numberImplementation = StandardPlugin.IMPLEMENTATION_NAIVE) private val mutableContext = MutableEvaluationContext(numberImplementation = StandardPlugin.IMPLEMENTATION_NAIVE, abacus = this)
/** /**
* The base context from which calculations are started. * The base context from which calculations are started.
*/ */
@@ -65,12 +63,13 @@ class Abacus(val configuration: Configuration) {
* Reloads the Abacus core. * Reloads the Abacus core.
*/ */
fun reload(){ fun reload(){
pluginManager.reload() with(mutableContext){
with(mutableContext) {
numberImplementation = pluginManager.numberImplementationFor(configuration.numberImplementation)
clearVariables() clearVariables()
clearDefinitions() clearDefinitions()
} }
pluginManager.reload()
mutableContext.numberImplementation = pluginManager.numberImplementationFor(configuration.numberImplementation)
?: StandardPlugin.IMPLEMENTATION_NAIVE
} }
/** /**
* Merges the current context with the provided one, updating * Merges the current context with the provided one, updating
@@ -87,7 +86,7 @@ class Abacus(val configuration: Configuration) {
* @param input the input string to parse * @param input the input string to parse
* @return the resulting tree, null if the tree builder or the produced tree are null. * @return the resulting tree, null if the tree builder or the produced tree are null.
*/ */
fun parseString(input: String): TreeNode? = treeBuilder.fromString(input) fun parseString(input: String): TreeNode = treeBuilder.fromString(input)
/** /**
* Evaluates the given tree. * Evaluates the given tree.
* *
@@ -106,9 +105,8 @@ class Abacus(val configuration: Configuration) {
* @return the evaluation result. * @return the evaluation result.
*/ */
fun evaluateTreeWithContext(tree: TreeNode, context: MutableEvaluationContext): EvaluationResult { fun evaluateTreeWithContext(tree: TreeNode, context: MutableEvaluationContext): EvaluationResult {
val newReducer = NumberReducer(this, context) val evaluationValue = tree.reduce(context)
val evaluationValue = tree.reduce(newReducer) return EvaluationResult(evaluationValue, context)
return EvaluationResult(evaluationValue, newReducer.context)
} }
} }

View File

@@ -1,4 +1,4 @@
package org.nwapw.abacus.tree package org.nwapw.abacus
import org.nwapw.abacus.context.MutableEvaluationContext import org.nwapw.abacus.context.MutableEvaluationContext
import org.nwapw.abacus.number.NumberInterface import org.nwapw.abacus.number.NumberInterface

View File

@@ -1,5 +1,6 @@
package org.nwapw.abacus.context package org.nwapw.abacus.context
import org.nwapw.abacus.exception.ContextException
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
/** /**
@@ -16,14 +17,14 @@ import kotlin.reflect.KProperty
*/ */
class ChainSearchDelegate<out V>(private val valueGetter: EvaluationContext.() -> V?) { class ChainSearchDelegate<out V>(private val valueGetter: EvaluationContext.() -> V?) {
operator fun getValue(selfRef: Any, property: KProperty<*>): V? { operator fun getValue(selfRef: Any, property: KProperty<*>): V {
var currentRef = selfRef as? EvaluationContext ?: return null var currentRef = selfRef as EvaluationContext
var returnedValue = currentRef.valueGetter() var returnedValue = currentRef.valueGetter()
while (returnedValue == null) { while (returnedValue == null) {
currentRef = currentRef.parent ?: break currentRef = currentRef.parent ?: break
returnedValue = currentRef.valueGetter() returnedValue = currentRef.valueGetter()
} }
return returnedValue return returnedValue ?: throw ContextException("context chain does not contain value")
} }
} }

View File

@@ -1,9 +1,10 @@
package org.nwapw.abacus.context package org.nwapw.abacus.context
import org.nwapw.abacus.Abacus
import org.nwapw.abacus.number.NumberInterface import org.nwapw.abacus.number.NumberInterface
import org.nwapw.abacus.plugin.NumberImplementation import org.nwapw.abacus.plugin.NumberImplementation
import org.nwapw.abacus.tree.Reducer import org.nwapw.abacus.tree.Reducer
import org.nwapw.abacus.tree.TreeNode import org.nwapw.abacus.tree.nodes.TreeNode
/** /**
* A context for the reduction of a [org.nwapw.abacus.tree.TreeNode] into a number. * A context for the reduction of a [org.nwapw.abacus.tree.TreeNode] into a number.
@@ -13,11 +14,11 @@ import org.nwapw.abacus.tree.TreeNode
* *
* @property parent the parent of this context. * @property parent the parent of this context.
* @property numberImplementation the implementation for numbers of this context. * @property numberImplementation the implementation for numbers of this context.
* @property reducer the reducer used by this context. * @property abacus the abacus instance used by this reducer.
*/ */
open class EvaluationContext(val parent: EvaluationContext? = null, abstract class EvaluationContext(val parent: EvaluationContext? = null,
open val numberImplementation: NumberImplementation? = null, open val numberImplementation: NumberImplementation? = null,
open val reducer: Reducer<NumberInterface>? = null) { open val abacus: Abacus? = null): Reducer<NumberInterface> {
/** /**
* The map of variables in this context. * The map of variables in this context.
@@ -43,14 +44,14 @@ open class EvaluationContext(val parent: EvaluationContext? = null,
/** /**
* The implementation inherited from this context's parent. * The implementation inherited from this context's parent.
*/ */
val inheritedNumberImplementation: NumberImplementation? val inheritedNumberImplementation: NumberImplementation
by ChainSearchDelegate { numberImplementation} by ChainSearchDelegate { numberImplementation }
/** /**
* The reducer inherited from this context's parent. * The Abacus instance inherited from this context's parent.
*/ */
val inheritedReducer: Reducer<NumberInterface>? val inheritedAbacus: Abacus
by ChainSearchDelegate { reducer } by ChainSearchDelegate { abacus }
/** /**
* The set of all variables in this context and its parents. * The set of all variables in this context and its parents.

View File

@@ -1,23 +1,26 @@
package org.nwapw.abacus.context package org.nwapw.abacus.context
import org.nwapw.abacus.Abacus
import org.nwapw.abacus.exception.NumberReducerException
import org.nwapw.abacus.exception.ReductionException
import org.nwapw.abacus.number.NumberInterface import org.nwapw.abacus.number.NumberInterface
import org.nwapw.abacus.plugin.NumberImplementation import org.nwapw.abacus.plugin.NumberImplementation
import org.nwapw.abacus.tree.Reducer import org.nwapw.abacus.tree.nodes.*
import org.nwapw.abacus.tree.TreeNode
/** /**
* A reduction context that is mutable. * A reduction context that is mutable.
*
* @param parent the parent of this context. * @param parent the parent of this context.
* @param numberImplementation the number implementation used in this context. * @param numberImplementation the number implementation used in this context.
* @param reducer the reducer used in this context * @param abacus the abacus instance used.
*/ */
class MutableEvaluationContext(parent: EvaluationContext? = null, class MutableEvaluationContext(parent: EvaluationContext? = null,
numberImplementation: NumberImplementation? = null, numberImplementation: NumberImplementation? = null,
reducer: Reducer<NumberInterface>? = null) : abacus: Abacus? = null) :
EvaluationContext(parent, numberImplementation, reducer) { PluginEvaluationContext(parent, numberImplementation, abacus) {
override var numberImplementation: NumberImplementation? = super.numberImplementation override var numberImplementation: NumberImplementation? = super.numberImplementation
override var reducer: Reducer<NumberInterface>? = super.reducer override var abacus: Abacus? = super.abacus
/** /**
* Writes data stored in the [other] context over data stored in this one. * Writes data stored in the [other] context over data stored in this one.
@@ -25,7 +28,6 @@ class MutableEvaluationContext(parent: EvaluationContext? = null,
*/ */
fun apply(other: EvaluationContext) { fun apply(other: EvaluationContext) {
if(other.numberImplementation != null) numberImplementation = other.numberImplementation if(other.numberImplementation != null) numberImplementation = other.numberImplementation
if(other.reducer != null) reducer = other.reducer
for(name in other.variables) { for(name in other.variables) {
setVariable(name, other.getVariable(name) ?: continue) setVariable(name, other.getVariable(name) ?: continue)
} }
@@ -34,36 +36,56 @@ class MutableEvaluationContext(parent: EvaluationContext? = null,
} }
} }
/** override fun reduceNode(treeNode: TreeNode, vararg children: Any): NumberInterface {
* Sets a variable to a certain [value]. val oldNumberImplementation = numberImplementation
* @param name the name of the variable. val abacus = inheritedAbacus
* @param value the value of the variable. val promotionManager = abacus.promotionManager
*/ val toReturn = when(treeNode){
fun setVariable(name: String, value: NumberInterface) { is NumberNode -> {
variableMap[name] = value inheritedNumberImplementation.instanceForString(treeNode.number)
} }
is VariableNode -> {
/** val variable = getVariable(treeNode.variable)
* Set a definition to a certain [value]. if(variable != null) return variable
* @param name the name of the definition. val definition = getDefinition(treeNode.variable)
* @param value the value of the definition. if(definition != null) return definition.reduce(this)
*/ throw NumberReducerException("variable is not defined.")
fun setDefinition(name: String, value: TreeNode) {
definitionMap[name] = value
} }
is NumberUnaryNode -> {
/** val child = children[0] as NumberInterface
* Clears the variables defined in this context. numberImplementation = abacus.pluginManager.interfaceImplementationFor(child.javaClass)
*/ abacus.pluginManager.operatorFor(treeNode.operation)
fun clearVariables(){ .apply(this, child)
variableMap.clear()
} }
is NumberBinaryNode -> {
/** val left = children[0] as NumberInterface
* Clears the definitions defined in this context. val right = children[1] as NumberInterface
*/ val promotionResult = promotionManager.promote(left, right)
fun clearDefinitions(){ numberImplementation = promotionResult.promotedTo
definitionMap.clear() abacus.pluginManager.operatorFor(treeNode.operation).apply(this, *promotionResult.items)
}
is NumberFunctionNode -> {
val promotionResult = promotionManager
.promote(*children.map { it as NumberInterface }.toTypedArray())
numberImplementation = promotionResult.promotedTo
abacus.pluginManager.functionFor(treeNode.callTo).apply(this, *promotionResult.items)
}
is TreeValueUnaryNode -> {
abacus.pluginManager.treeValueOperatorFor(treeNode.operation)
.apply(this, treeNode.applyTo)
}
is TreeValueBinaryNode -> {
abacus.pluginManager.treeValueOperatorFor(treeNode.operation)
.apply(this, treeNode.left, treeNode.right)
}
is TreeValueFunctionNode -> {
abacus.pluginManager.treeValueFunctionFor(treeNode.callTo)
.apply(this, *treeNode.children.toTypedArray())
}
else -> throw ReductionException("unrecognized tree node.")
}
numberImplementation = oldNumberImplementation
return toReturn
} }
} }

View File

@@ -0,0 +1,58 @@
package org.nwapw.abacus.context
import org.nwapw.abacus.Abacus
import org.nwapw.abacus.number.NumberInterface
import org.nwapw.abacus.plugin.NumberImplementation
import org.nwapw.abacus.tree.nodes.TreeNode
/**
* An evaluation context with limited mutability.
*
* An evaluation context that is mutable but in a limited way, that is, not allowing the modifications
* of variables whose changes might cause issues outside of the function. An example of this would be
* the modification of the [numberImplementation], which would cause code paths such as the parsing
* of NumberNodes to produce a different type of number than if the function did not run, whcih is unacceptable.
*
* @param parent the parent of this context.
* @param numberImplementation the number implementation used in this context.
* @param abacus the abacus instance used.
*/
abstract class PluginEvaluationContext(parent: EvaluationContext? = null,
numberImplementation: NumberImplementation? = null,
abacus: Abacus? = null) :
EvaluationContext(parent, numberImplementation, abacus) {
/**
* Sets a variable to a certain [value].
* @param name the name of the variable.
* @param value the value of the variable.
*/
fun setVariable(name: String, value: NumberInterface) {
variableMap[name] = value
}
/**
* Set a definition to a certain [value].
* @param name the name of the definition.
* @param value the value of the definition.
*/
fun setDefinition(name: String, value: TreeNode) {
definitionMap[name] = value
}
/**
* Clears the variables defined in this context.
*/
fun clearVariables(){
variableMap.clear()
}
/**
* Clears the definitions defined in this context.
*/
fun clearDefinitions(){
definitionMap.clear()
}
}

View File

@@ -1,6 +1,7 @@
package org.nwapw.abacus.function.applicable package org.nwapw.abacus.function
import org.nwapw.abacus.context.MutableEvaluationContext import org.nwapw.abacus.context.MutableEvaluationContext
import org.nwapw.abacus.context.PluginEvaluationContext
import org.nwapw.abacus.exception.DomainException import org.nwapw.abacus.exception.DomainException
/** /**
@@ -18,7 +19,7 @@ interface Applicable<in T : Any, out O : Any> {
* @param params the parameter array to verify for compatibility. * @param params the parameter array to verify for compatibility.
* @return whether the array can be used with applyInternal. * @return whether the array can be used with applyInternal.
*/ */
fun matchesParams(context: MutableEvaluationContext, params: Array<out T>): Boolean fun matchesParams(context: PluginEvaluationContext, params: Array<out T>): Boolean
/** /**
* Applies the applicable object to the given parameters, * Applies the applicable object to the given parameters,
@@ -26,7 +27,7 @@ interface Applicable<in T : Any, out O : Any> {
* @param params the parameters to apply to. * @param params the parameters to apply to.
* @return the result of the application. * @return the result of the application.
*/ */
fun applyInternal(context: MutableEvaluationContext, params: Array<out T>): O fun applyInternal(context: PluginEvaluationContext, params: Array<out T>): O
/** /**
* If the parameters can be used with this applicable, returns * If the parameters can be used with this applicable, returns
@@ -35,7 +36,7 @@ interface Applicable<in T : Any, out O : Any> {
* @param params the parameters to apply to. * @param params the parameters to apply to.
* @return the result of the operation, or null if parameters do not match. * @return the result of the operation, or null if parameters do not match.
*/ */
fun apply(context: MutableEvaluationContext, vararg params: T): O { fun apply(context: PluginEvaluationContext, vararg params: T): O {
if (!matchesParams(context, params)) if (!matchesParams(context, params))
throw DomainException("parameters do not match function requirements.") throw DomainException("parameters do not match function requirements.")
return applyInternal(context, params) return applyInternal(context, params)

View File

@@ -1,6 +1,6 @@
package org.nwapw.abacus.function package org.nwapw.abacus.function.interfaces
import org.nwapw.abacus.function.applicable.Applicable import org.nwapw.abacus.function.Applicable
import org.nwapw.abacus.number.NumberInterface import org.nwapw.abacus.number.NumberInterface
/** /**

View File

@@ -1,6 +1,9 @@
package org.nwapw.abacus.function package org.nwapw.abacus.function.interfaces
import org.nwapw.abacus.function.applicable.Applicable import org.nwapw.abacus.function.Applicable
import org.nwapw.abacus.function.Operator
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.number.NumberInterface import org.nwapw.abacus.number.NumberInterface
/** /**

View File

@@ -1,8 +1,8 @@
package org.nwapw.abacus.function package org.nwapw.abacus.function.interfaces
import org.nwapw.abacus.function.applicable.Applicable import org.nwapw.abacus.function.Applicable
import org.nwapw.abacus.number.NumberInterface import org.nwapw.abacus.number.NumberInterface
import org.nwapw.abacus.tree.TreeNode import org.nwapw.abacus.tree.nodes.TreeNode
/** /**
* A function that operates on trees. * A function that operates on trees.

View File

@@ -1,8 +1,11 @@
package org.nwapw.abacus.function package org.nwapw.abacus.function.interfaces
import org.nwapw.abacus.function.applicable.Applicable import org.nwapw.abacus.function.Applicable
import org.nwapw.abacus.function.Operator
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.number.NumberInterface import org.nwapw.abacus.number.NumberInterface
import org.nwapw.abacus.tree.TreeNode import org.nwapw.abacus.tree.nodes.TreeNode
/** /**
* An operator that operates on trees. * An operator that operates on trees.

View File

@@ -1,73 +1,42 @@
package org.nwapw.abacus.number; package org.nwapw.abacus.number
import org.nwapw.abacus.exception.ComputationInterruptedException; import org.nwapw.abacus.exception.ComputationInterruptedException
import org.nwapw.abacus.number.range.NumberRangeBuilder
/** abstract class NumberInterface: Comparable<NumberInterface> {
* An interface used to represent a number.
*/
public abstract class NumberInterface {
/** /**
* Check if the thread was interrupted and * Check if the thread was interrupted and
* throw an exception to end the computation. * throw an exception to end the computation.
*/ */
private static void checkInterrupted() { private fun checkInterrupted(){
if (Thread.currentThread().isInterrupted()) if(Thread.currentThread().isInterrupted)
throw new ComputationInterruptedException(); throw ComputationInterruptedException()
} }
/**
* Returns the integer representation of this number, discarding any fractional part,
* if int can hold the value.
*
* @return the integer value of this number.
*/
abstract fun intValue(): Int
/**
* Same as Math.signum().
*
* @return 1 if this number is positive, -1 if this number is negative, 0 if this number is 0.
*/
abstract fun signum(): Int
/** /**
* The maximum precision to which this number operates. * The maximum precision to which this number operates.
*
* @return the precision.
*/ */
public abstract int getMaxPrecision(); abstract val maxPrecision: Int
/** /**
* Multiplies this number by another, returning * Returns the smallest error this instance can tolerate depending
* a new number instance. * on its precision and value.
*
* @param multiplier the multiplier
* @return the result of the multiplication.
*/ */
protected abstract NumberInterface multiplyInternal(NumberInterface multiplier); abstract val maxError: NumberInterface
/**
* Multiplies this number by another, returning
* a new number instance. Also, checks if the
* thread has been interrupted, and if so, throws
* an exception.
*
* @param multiplier the multiplier
* @return the result of the multiplication.
*/
public final NumberInterface multiply(NumberInterface multiplier) {
checkInterrupted();
return multiplyInternal(multiplier);
}
/**
* Divides this number by another, returning
* a new number instance.
*
* @param divisor the divisor
* @return the result of the division.
*/
protected abstract NumberInterface divideInternal(NumberInterface divisor);
/**
* Divides this number by another, returning
* a new number instance. Also, checks if the
* thread has been interrupted, and if so, throws
* an exception.
*
* @param divisor the divisor
* @return the result of the division.
*/
public final NumberInterface divide(NumberInterface divisor) {
checkInterrupted();
return divideInternal(divisor);
}
/** /**
* Adds this number to another, returning * Adds this number to another, returning
@@ -76,22 +45,7 @@ public abstract class NumberInterface {
* @param summand the summand * @param summand the summand
* @return the result of the summation. * @return the result of the summation.
*/ */
protected abstract NumberInterface addInternal(NumberInterface summand); abstract fun addInternal(summand: NumberInterface): NumberInterface
/**
* Adds this number to another, returning
* a new number instance. Also, checks if the
* thread has been interrupted, and if so, throws
* an exception.
*
* @param summand the summand
* @return the result of the summation.
*/
public final NumberInterface add(NumberInterface summand) {
checkInterrupted();
return addInternal(summand);
}
/** /**
* Subtracts another number from this number, * Subtracts another number from this number,
* a new number instance. * a new number instance.
@@ -99,30 +53,111 @@ public abstract class NumberInterface {
* @param subtrahend the subtrahend. * @param subtrahend the subtrahend.
* @return the result of the subtraction. * @return the result of the subtraction.
*/ */
protected abstract NumberInterface subtractInternal(NumberInterface subtrahend); abstract fun subtractInternal(subtrahend: NumberInterface): NumberInterface
/** /**
* Subtracts another number from this number, * Multiplies this number by another, returning
* a new number instance. Also, checks if the * a new number instance.
* thread has been interrupted, and if so, throws
* an exception.
* *
* @param subtrahend the subtrahend. * @param multiplier the multiplier
* @return the result of the subtraction. * @return the result of the multiplication.
*/ */
public final NumberInterface subtract(NumberInterface subtrahend) { abstract fun multiplyInternal(multiplier: NumberInterface): NumberInterface
checkInterrupted(); /**
return subtractInternal(subtrahend); * Divides this number by another, returning
} * a new number instance.
*
* @param divisor the divisor
* @return the result of the division.
*/
abstract fun divideInternal(divisor: NumberInterface): NumberInterface
/** /**
* Returns a new instance of this number with * Returns a new instance of this number with
* the sign flipped. * the sign flipped.
* *
* @return the new instance. * @return the new instance.
*/ */
protected abstract NumberInterface negateInternal(); abstract fun negateInternal(): NumberInterface
/**
* Raises this number to an integer power.
*
* @param exponent the exponent to which to take the number.
* @return the resulting value.
*/
abstract fun intPowInternal(pow: Int): NumberInterface
/**
* Returns the least integer greater than or equal to the number.
*
* @return the least integer greater or equal to the number, if int can hold the value.
*/
abstract fun ceilingInternal(): NumberInterface
/**
* Return the greatest integer less than or equal to the number.
*
* @return the greatest integer smaller or equal the number.
*/
abstract fun floorInternal(): NumberInterface
/**
* Returns the fractional part of the number.
*
* @return the fractional part of the number.
*/
abstract fun fractionalPartInternal(): NumberInterface
/**
* Adds this number to another, returning
* a new number instance. Also, checks if the
* thread has been interrupted, and if so, throws
* an exception.
*
* @param summand the summand
* @return the result of the summation.
*/
fun add(summand: NumberInterface): NumberInterface {
checkInterrupted()
return addInternal(summand)
}
/**
* Subtracts another number from this number,
* a new number instance. Also, checks if the
* thread has been interrupted, and if so, throws
* an exception.
*
* @param subtrahend the subtrahend.
* @return the result of the subtraction.
*/
fun subtract(subtrahend: NumberInterface): NumberInterface {
checkInterrupted()
return subtractInternal(subtrahend)
}
/**
* Multiplies this number by another, returning
* a new number instance. Also, checks if the
* thread has been interrupted, and if so, throws
* an exception.
*
* @param multiplier the multiplier
* @return the result of the multiplication.
*/
fun multiply(multiplier: NumberInterface): NumberInterface {
checkInterrupted()
return multiplyInternal(multiplier)
}
/**
* Divides this number by another, returning
* a new number instance. Also, checks if the
* thread has been interrupted, and if so, throws
* an exception.
*
* @param divisor the divisor
* @return the result of the division.
*/
fun divide(divisor: NumberInterface): NumberInterface {
checkInterrupted()
return divideInternal(divisor)
}
/** /**
* Returns a new instance of this number with * Returns a new instance of this number with
@@ -132,19 +167,11 @@ public abstract class NumberInterface {
* *
* @return the new instance. * @return the new instance.
*/ */
public final NumberInterface negate() { fun negate(): NumberInterface {
checkInterrupted(); checkInterrupted()
return negateInternal(); return negateInternal()
} }
/**
* Raises this number to an integer power.
*
* @param exponent the exponent to which to take the number.
* @return the resulting value.
*/
protected abstract NumberInterface intPowInternal(int exponent);
/** /**
* Raises this number to an integer power. Also, checks if the * Raises this number to an integer power. Also, checks if the
* thread has been interrupted, and if so, throws * thread has been interrupted, and if so, throws
@@ -153,33 +180,11 @@ public abstract class NumberInterface {
* @param exponent the exponent to which to take the number. * @param exponent the exponent to which to take the number.
* @return the resulting value. * @return the resulting value.
*/ */
public final NumberInterface intPow(int exponent) { fun intPow(exponent: Int): NumberInterface {
checkInterrupted(); checkInterrupted()
return intPowInternal(exponent); return intPowInternal(exponent)
} }
/**
* Compares this number to another.
*
* @param number the number to compare to.
* @return same as Integer.compare();
*/
public abstract 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.
*/
public abstract int signum();
/**
* Returns the least integer greater than or equal to the number.
*
* @return the least integer greater or equal to the number, if int can hold the value.
*/
protected abstract NumberInterface ceilingInternal();
/** /**
* Returns the least integer greater than or equal to the number. * Returns the least integer greater than or equal to the number.
* Also, checks if the thread has been interrupted, and if so, throws * Also, checks if the thread has been interrupted, and if so, throws
@@ -187,18 +192,11 @@ public abstract class NumberInterface {
* *
* @return the least integer bigger or equal to the number. * @return the least integer bigger or equal to the number.
*/ */
public final NumberInterface ceiling() { fun ceiling(): NumberInterface {
checkInterrupted(); checkInterrupted()
return ceilingInternal(); return ceilingInternal()
} }
/**
* Return the greatest integer less than or equal to the number.
*
* @return the greatest integer smaller or equal the number.
*/
protected abstract NumberInterface floorInternal();
/** /**
* Return the greatest integer less than or equal to the number. * Return the greatest integer less than or equal to the number.
* Also, checks if the thread has been interrupted, and if so, throws * Also, checks if the thread has been interrupted, and if so, throws
@@ -206,18 +204,11 @@ public abstract class NumberInterface {
* *
* @return the greatest int smaller than or equal to the number. * @return the greatest int smaller than or equal to the number.
*/ */
public final NumberInterface floor() { fun floor(): NumberInterface {
checkInterrupted(); checkInterrupted()
return floorInternal(); return floorInternal()
} }
/**
* Returns the fractional part of the number.
*
* @return the fractional part of the number.
*/
protected abstract NumberInterface fractionalPartInternal();
/** /**
* Returns the fractional part of the number, specifically x - floor(x). * Returns the fractional part of the number, specifically x - floor(x).
* Also, checks if the thread has been interrupted, * Also, checks if the thread has been interrupted,
@@ -225,25 +216,61 @@ public abstract class NumberInterface {
* *
* @return the fractional part of the number. * @return the fractional part of the number.
*/ */
public final NumberInterface fractionalPart() { fun fractionalPart(): NumberInterface {
checkInterrupted(); checkInterrupted()
return fractionalPartInternal(); return fractionalPartInternal()
} }
/** /**
* Returns the integer representation of this number, discarding any fractional part, * Checks whether the given number is an integer or not.
* if int can hold the value.
* *
* @return the integer value of this number. * @return whether the number is an integer or not.
*/ */
public abstract int intValue(); fun isInteger() = fractionalPart().signum() == 0
/** /**
* Returns the smallest error this instance can tolerate depending * Returns a NumberRangeBuilder object, which is used to create a range.
* on its precision and value. * The reason that this returns a builder and not an actual range is that
* * the NumberRange needs to promote values passed to it, which
* @return the smallest error that should be permitted in calculations. * requires an abacus instance.
* @param other the value at the bottom of the range.
* @return the resulting range builder.
*/ */
public abstract NumberInterface getMaxError(); operator fun rangeTo(other: NumberInterface) = NumberRangeBuilder(this, other)
/**
* Plus operator overloaded to allow "nice" looking math.
* @param other the value to add to this number.
* @return the result of the addition.
*/
operator fun plus(other: NumberInterface) = add(other)
/**
* Minus operator overloaded to allow "nice" looking math.
* @param other the value to subtract to this number.
* @return the result of the subtraction.
*/
operator fun minus(other: NumberInterface) = subtract(other)
/**
* Times operator overloaded to allow "nice" looking math.
* @param other the value to multiply this number by.
* @return the result of the multiplication.
*/
operator fun times(other: NumberInterface) = multiply(other)
/**
* Divide operator overloaded to allow "nice" looking math.
* @param other the value to divide this number by.
* @return the result of the division.
*/
operator fun div(other: NumberInterface) = divide(other)
/**
* The plus operator.
* @return this number.
*/
operator fun unaryPlus() = this
/**
* The minus operator.
* @return the negative of this number.
*/
operator fun unaryMinus() = negate()
} }

View File

@@ -1,7 +1,8 @@
@file:JvmName("NumberUtils") @file:JvmName("NumberUtils")
package org.nwapw.abacus.number package org.nwapw.abacus.number.promotion
import org.nwapw.abacus.number.NumberInterface
typealias PromotionFunction = java.util.function.Function<NumberInterface, NumberInterface>
typealias PromotionPath = List<PromotionFunction> typealias PromotionPath = List<PromotionFunction>
typealias NumberClass = Class<NumberInterface> typealias NumberClass = Class<NumberInterface>
@@ -12,5 +13,5 @@ typealias NumberClass = Class<NumberInterface>
* @param from the number to start from. * @param from the number to start from.
*/ */
fun PromotionPath.promote(from: NumberInterface): NumberInterface { fun PromotionPath.promote(from: NumberInterface): NumberInterface {
return fold(from, { current, function -> function.apply(current) }) return fold(from, { current, function -> function.promote(current) })
} }

View File

@@ -0,0 +1,20 @@
package org.nwapw.abacus.number.promotion
import org.nwapw.abacus.number.NumberInterface
/**
* Function that is used to promote a number from one type to another.
*
* A promotion function is used in the promotion system as a mean to
* actually "travel" down the promotion path.
*/
interface PromotionFunction {
/**
* Promotes the given [number] into another type.
* @param number the number to promote from.
* @return the new number with the same value.
*/
fun promote(number: NumberInterface): NumberInterface
}

View File

@@ -1,10 +1,11 @@
package org.nwapw.abacus.number package org.nwapw.abacus.number.promotion
import org.nwapw.abacus.Abacus import org.nwapw.abacus.Abacus
import org.nwapw.abacus.exception.PromotionException
import org.nwapw.abacus.number.NumberInterface
import org.nwapw.abacus.plugin.NumberImplementation import org.nwapw.abacus.plugin.NumberImplementation
import org.nwapw.abacus.plugin.PluginListener import org.nwapw.abacus.plugin.PluginListener
import org.nwapw.abacus.plugin.PluginManager import org.nwapw.abacus.plugin.PluginManager
import java.util.function.Function
/** /**
* A class that handles promotions based on priority and the * A class that handles promotions based on priority and the
@@ -30,28 +31,17 @@ class PromotionManager(val abacus: Abacus) : PluginListener {
val fromName = abacus.pluginManager.interfaceImplementationNameFor(from.implementation) val fromName = abacus.pluginManager.interfaceImplementationNameFor(from.implementation)
val toName = abacus.pluginManager.interfaceImplementationNameFor(to.implementation) val toName = abacus.pluginManager.interfaceImplementationNameFor(to.implementation)
if(fromName == toName) return listOf(Function { it }) if(fromName == toName) return listOf(object : PromotionFunction {
override fun promote(number: NumberInterface): NumberInterface {
return number
}
})
if(from.promotionPaths.containsKey(toName)) if(from.promotionPaths.containsKey(toName))
return listOf(from.promotionPaths[toName] ?: return null) return listOf(from.promotionPaths[toName] ?: return null)
return null return null
} }
/**
* If a path between the given implementations has already been computed, uses
* the already calculated path. Otherwise, calls [computePathBetween] to compute a new
* path.
*
* @param from the implementation to start from.
* @param to the implementation to get to.
* @return the resulting promotion path, or null if it is not found
*/
fun getPathBetween(from: NumberImplementation, to: NumberImplementation): PromotionPath? {
return computePaths.computeIfAbsent(from to to, {
computePathBetween(it.first, it.second)
})
}
/** /**
* Promote all the numbers in the list to the same number implementation, to ensure * Promote all the numbers in the list to the same number implementation, to ensure
* they can be used with each other. Finds the highest priority implementation * they can be used with each other. Finds the highest priority implementation
@@ -60,22 +50,30 @@ class PromotionManager(val abacus: Abacus) : PluginListener {
* @param numbers the numbers to promote. * @param numbers the numbers to promote.
* @return the resulting promotion result. * @return the resulting promotion result.
*/ */
fun promote(vararg numbers: NumberInterface): PromotionResult? { fun promote(vararg numbers: NumberInterface): PromotionResult {
val pluginManager = abacus.pluginManager val pluginManager = abacus.pluginManager
val implementations = numbers.map { pluginManager.interfaceImplementationFor(it.javaClass) } val implementations = numbers.map { pluginManager.interfaceImplementationFor(it.javaClass) }
val highestPriority = implementations.sortedBy { it.priority }.last() val highestPriority = implementations.sortedBy { it.priority }.last()
return PromotionResult(items = numbers.map { return PromotionResult(items = numbers.map {
if(it.javaClass == highestPriority.implementation) it if (it.javaClass == highestPriority.implementation) it
else getPathBetween(pluginManager.interfaceImplementationFor(it.javaClass), highestPriority) else computePaths[pluginManager.interfaceImplementationFor(it.javaClass) to highestPriority]
?.promote(it) ?: return null ?.promote(it) ?: throw PromotionException()
}.toTypedArray(), promotedTo = highestPriority) }.toTypedArray(), promotedTo = highestPriority)
} }
override fun onLoad(manager: PluginManager?) { override fun onLoad(manager: PluginManager) {
val implementations = manager.allNumberImplementations.map { manager.numberImplementationFor(it) }
for(first in implementations) {
for(second in implementations) {
val promoteFrom = if(second.priority > first.priority) first else second
val promoteTo = if(second.priority > first.priority) second else first
val path = computePathBetween(promoteFrom, promoteTo)
computePaths[promoteFrom to promoteTo] = path
}
}
} }
override fun onUnload(manager: PluginManager?) { override fun onUnload(manager: PluginManager) {
computePaths.clear() computePaths.clear()
} }

View File

@@ -1,5 +1,6 @@
package org.nwapw.abacus.number package org.nwapw.abacus.number.promotion
import org.nwapw.abacus.number.NumberInterface
import org.nwapw.abacus.plugin.NumberImplementation import org.nwapw.abacus.plugin.NumberImplementation
/** /**

View File

@@ -0,0 +1,29 @@
package org.nwapw.abacus.number.range
import org.nwapw.abacus.Abacus
import org.nwapw.abacus.number.NumberInterface
/**
* A closed range designed specifically for [NumberInterface]
*
* Besides providing the usual functionality of a [ClosedRange], this range
* also handles promotion - that is, it's safe to use it with numbers of different
* implementations, even as starting and ending points.
*
* @property abacus the abacus instance used for promotion.
* @property start the starting point of the range.
* @property endInclusive the ending point of the range.
*/
class NumberRange(val abacus: Abacus,
override val start: NumberInterface,
override val endInclusive: NumberInterface): ClosedRange<NumberInterface> {
override operator fun contains(value: NumberInterface): Boolean {
val promotionResult = abacus.promotionManager.promote(start, endInclusive, value)
val newStart = promotionResult.items[0]
val newEnd = promotionResult.items[1]
val newValue = promotionResult.items[2]
return newValue >= newStart && newValue <= newEnd
}
}

View File

@@ -0,0 +1,25 @@
package org.nwapw.abacus.number.range
import org.nwapw.abacus.Abacus
import org.nwapw.abacus.number.NumberInterface
/**
* A utility class for creating [NumberRange] instances.
*
* Unlike a regular [ClosedRange], a NumberRange must have a third parameter,
* which is the [Abacus] instance that is used for promotion. However, the ".." operator
* is infix, and can only take two parameters. The solution is, instead of returning instances
* of NumberRange directly, to return a builder, which then provides a [with] infix function
* to attach it to an instance of Abacus.
* @property start the beginning of the range.
* @property endInclusive the end of the range.
*/
class NumberRangeBuilder(private val start: NumberInterface, private val endInclusive: NumberInterface) {
/**
* Generate a [NumberRange] with the given instance of [abacus].
* @return a new range with the given instance of Abacus.
*/
infix fun with(abacus: Abacus) = NumberRange(abacus, start, endInclusive)
}

View File

@@ -1,16 +1,17 @@
package org.nwapw.abacus.parsing; package org.nwapw.abacus.parsing
import org.nwapw.abacus.tree.TreeNode; import org.nwapw.abacus.tree.nodes.TreeNode
import java.util.List;
/** /**
* An itnerface that provides the ability to convert a list of tokens * Converter from tokens into a parse tree.
*
* An interface that provides the ability to convert a list of tokens
* into a parse tree. * into a parse tree.
* *
* @param <T> the type of tokens accepted by this parser. * @param <T> the type of tokens accepted by this parser.
*/ */
public interface Parser<T> {
interface Parser<in T> {
/** /**
* Constructs a tree out of the given tokens. * Constructs a tree out of the given tokens.
@@ -18,5 +19,6 @@ public interface Parser<T> {
* @param tokens the tokens to construct a tree from. * @param tokens the tokens to construct a tree from.
* @return the constructed tree, or null on error. * @return the constructed tree, or null on error.
*/ */
public TreeNode constructTree(List<T> tokens); fun constructTree(tokens: List<T>): TreeNode
} }

View File

@@ -0,0 +1,21 @@
package org.nwapw.abacus.parsing
/**
* Converter from string to tokens.
*
* Interface that converts a string into a list
* of tokens of a certain type.
*
* @param <T> the type of the tokens produced.
*/
interface Tokenizer<out T> {
/**
* Converts a string into tokens.
*
* @param string the string to convert.
* @return the list of tokens, or null on error.
*/
fun tokenizeString(string: String): List<T>
}

View File

@@ -0,0 +1,26 @@
package org.nwapw.abacus.parsing
import org.nwapw.abacus.tree.nodes.TreeNode
/**
* Class to combine a [Tokenizer] and a [Parser]
*
* TreeBuilder class used to piece together a Tokenizer and
* Parser of the same kind. This is used to essentially avoid
* 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 <T> the type of tokens created by the tokenizer and used by the parser.
*/
class TreeBuilder<T>(private val tokenizer: Tokenizer<T>, private val parser: Parser<T>) {
/**
* Parses the given [string] into a tree.
*
* @param string the string to parse into a tree.
* @return the resulting tree.
*/
fun fromString(string: String): TreeNode = parser.constructTree(tokenizer.tokenizeString(string))
}

View File

@@ -0,0 +1,21 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.PluginEvaluationContext
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.function.interfaces.NumberOperator
import org.nwapw.abacus.number.NumberInterface
/**
* The addition operator.
*
* This is a standard operator that simply performs addition.
*/
class OperatorAdd: NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1) {
override fun matchesParams(context: PluginEvaluationContext, params: Array<out NumberInterface>) =
params.size == 2
override fun applyInternal(context: PluginEvaluationContext, params: Array<out NumberInterface>) =
params[0] + params[1]
}

View File

@@ -0,0 +1,38 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.PluginEvaluationContext
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.function.interfaces.NumberOperator
import org.nwapw.abacus.number.NumberInterface
import org.nwapw.abacus.plugin.standard.StandardPlugin.*
/**
* The power operator.
*
* This is a standard operator that brings one number to the power of the other.
*/
class OperatorCaret: NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 3) {
override fun matchesParams(context: PluginEvaluationContext, params: Array<out NumberInterface>) =
params.size == 2
&& !(params[0].signum() == 0 && params[1].signum() == 0)
&& !(params[0].signum() == -1 && !params[1].isInteger())
override fun applyInternal(context: PluginEvaluationContext, params: Array<out NumberInterface>): NumberInterface {
val implementation = context.inheritedNumberImplementation
if (params[0].signum() == 0)
return implementation.instanceForString("0")
else if (params[1].signum() == 0)
return implementation.instanceForString("1")
//Detect integer bases:
if (params[0].isInteger()
&& FUNCTION_ABS.apply(context, params[1]) < implementation.instanceForString(Integer.toString(Integer.MAX_VALUE))
&& FUNCTION_ABS.apply(context, params[1]) >= implementation.instanceForString("1")) {
val newParams = arrayOf(params[0], params[1].fractionalPart())
return params[0].intPow(params[1].floor().intValue()) * applyInternal(context, newParams)
}
return FUNCTION_EXP.apply(context, FUNCTION_LN.apply(context, FUNCTION_ABS.apply(context, params[0])) * params[1])
}
}

View File

@@ -0,0 +1,28 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.PluginEvaluationContext
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.function.interfaces.TreeValueOperator
import org.nwapw.abacus.number.NumberInterface
import org.nwapw.abacus.tree.nodes.TreeNode
import org.nwapw.abacus.tree.nodes.VariableNode
/**
* The definition operator.
*
* This is a standard operator that creates a definition - something that doesn't capture variable values
* when it's created, but rather the variables themselves, and changes when the variables it uses change.
*/
class OperatorDefine: TreeValueOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) {
override fun matchesParams(context: PluginEvaluationContext, params: Array<out TreeNode>) =
params.size == 2 && params[0] is VariableNode
override fun applyInternal(context: PluginEvaluationContext, params: Array<out TreeNode>): NumberInterface {
val assignTo = (params[0] as VariableNode).variable
context.setDefinition(assignTo, params[1])
return params[1].reduce(context)
}
}

View File

@@ -0,0 +1,21 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.PluginEvaluationContext
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.function.interfaces.NumberOperator
import org.nwapw.abacus.number.NumberInterface
/**
* The division operator.
*
* This is a standard operator that simply performs division.
*/
class OperatorDivide: NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 2) {
override fun matchesParams(context: PluginEvaluationContext, params: Array<out NumberInterface>) =
params.size == 2
override fun applyInternal(context: PluginEvaluationContext, params: Array<out NumberInterface>) =
params[0] / params[1]
}

View File

@@ -0,0 +1,37 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.PluginEvaluationContext
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.function.interfaces.NumberOperator
import org.nwapw.abacus.number.NumberInterface
/**
* The factorial operator.
*
* This is a standard operator that simply evaluates the factorial of a number.
*/
class OperatorFactorial: NumberOperator(OperatorAssociativity.LEFT, OperatorType.UNARY_POSTFIX, 0) {
override fun matchesParams(context: PluginEvaluationContext, params: Array<out NumberInterface>) =
params.size == 1
&& params[0].isInteger()
&& params[0].signum() >= 0
override fun applyInternal(context: PluginEvaluationContext, params: Array<out NumberInterface>): NumberInterface {
val implementation = context.inheritedNumberImplementation
val one = implementation.instanceForString("1")
if (params[0].signum() == 0) {
return one
}
var factorial = params[0]
var multiplier = params[0] - one
//It is necessary to later prevent calls of factorial on anything but non-negative integers.
while (multiplier.signum() == 1) {
factorial *= multiplier
multiplier -= one
}
return factorial
}
}

View File

@@ -0,0 +1,21 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.PluginEvaluationContext
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.function.interfaces.NumberOperator
import org.nwapw.abacus.number.NumberInterface
/**
* The multiplication operator.
*
* This is a standard operator that simply performs multiplication.
*/
class OperatorMultiply: NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 2) {
override fun matchesParams(context: PluginEvaluationContext, params: Array<out NumberInterface>) =
params.size == 2
override fun applyInternal(context: PluginEvaluationContext, params: Array<out NumberInterface>) =
params[0] * params[1]
}

View File

@@ -0,0 +1,26 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.PluginEvaluationContext
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.function.interfaces.NumberOperator
import org.nwapw.abacus.number.NumberInterface
import org.nwapw.abacus.plugin.standard.StandardPlugin.OP_FACTORIAL
import org.nwapw.abacus.plugin.standard.StandardPlugin.OP_NPR
/**
* The "N choose R" operator.
*
* This is a standard operator that returns the number of possible combinations, regardless of order,
* of a certain size can be taken out of a pool of a bigger size.
*/
class OperatorNcr: NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 1) {
override fun matchesParams(context: PluginEvaluationContext, params: Array<out NumberInterface>) =
params.size == 2 && params[0].isInteger()
&& params[1].isInteger()
override fun applyInternal(context: PluginEvaluationContext, params: Array<out NumberInterface>) =
OP_NPR.apply(context, *params) / OP_FACTORIAL.apply(context, params[1])
}

View File

@@ -0,0 +1,22 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.PluginEvaluationContext
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.function.interfaces.NumberOperator
import org.nwapw.abacus.number.NumberInterface
/**
* The negation operator.
*
* This is a standard operator that negates a number.
*/
class OperatorNegate: NumberOperator(OperatorAssociativity.LEFT, OperatorType.UNARY_PREFIX, 1) {
override fun matchesParams(context: PluginEvaluationContext, params: Array<out NumberInterface>) =
params.size == 1
override fun applyInternal(context: PluginEvaluationContext, params: Array<out NumberInterface>) =
-params[0]
}

View File

@@ -0,0 +1,42 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.PluginEvaluationContext
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.function.interfaces.NumberOperator
import org.nwapw.abacus.number.NumberInterface
/**
* The "N pick R" operator.
*
* his is a standard operator that returns the number of possible combinations
* of a certain size can be taken out of a pool of a bigger size.
*/
class OperatorNpr: NumberOperator(OperatorAssociativity.RIGHT, OperatorType.BINARY_INFIX, 1) {
override fun matchesParams(context: PluginEvaluationContext, params: Array<out NumberInterface>) =
params.size == 2 && params[0].isInteger()
&& params[1].isInteger()
override fun applyInternal(context: PluginEvaluationContext, params: Array<out NumberInterface>): NumberInterface {
val implementation = context.inheritedNumberImplementation
if (params[0] < params[1] ||
params[0].signum() < 0 ||
params[0].signum() == 0 && params[1].signum() != 0)
return implementation.instanceForString("0")
var total = implementation.instanceForString("1")
var multiplyBy = params[0]
var remainingMultiplications = params[1]
val halfway = params[0] / implementation.instanceForString("2")
if (remainingMultiplications > halfway) {
remainingMultiplications = params[0] - remainingMultiplications
}
while (remainingMultiplications.signum() > 0) {
total *= multiplyBy
remainingMultiplications -= implementation.instanceForString("1")
multiplyBy -= implementation.instanceForString("1")
}
return total
}
}

View File

@@ -0,0 +1,28 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.PluginEvaluationContext
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.function.interfaces.TreeValueOperator
import org.nwapw.abacus.number.NumberInterface
import org.nwapw.abacus.tree.nodes.TreeNode
import org.nwapw.abacus.tree.nodes.VariableNode
/**
* The set operator.
*
* This is a standard operator that assigns a value to a variable name.
*/
class OperatorSet: TreeValueOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) {
override fun matchesParams(context: PluginEvaluationContext, params: Array<out TreeNode>) =
params.size == 2 && params[0] is VariableNode
override fun applyInternal(context: PluginEvaluationContext, params: Array<out TreeNode>): NumberInterface {
val assignTo = (params[0] as VariableNode).variable
val value = params[1].reduce(context)
context.setVariable(assignTo, value)
return value
}
}

View File

@@ -0,0 +1,21 @@
package org.nwapw.abacus.plugin.standard.operator
import org.nwapw.abacus.context.PluginEvaluationContext
import org.nwapw.abacus.function.OperatorAssociativity
import org.nwapw.abacus.function.OperatorType
import org.nwapw.abacus.function.interfaces.NumberOperator
import org.nwapw.abacus.number.NumberInterface
/**
* The subtraction operator.
*
* This is a standard operator that performs subtraction.
*/
class OperatorSubtract: NumberOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 1) {
override fun matchesParams(context: PluginEvaluationContext, params: Array<out NumberInterface>) =
params.size == 2
override fun applyInternal(context: PluginEvaluationContext, params: Array<out NumberInterface>) =
params[0] - params[1]
}

View File

@@ -1,67 +0,0 @@
package org.nwapw.abacus.tree
import org.nwapw.abacus.Abacus
import org.nwapw.abacus.context.EvaluationContext
import org.nwapw.abacus.exception.EvaluationException
import org.nwapw.abacus.number.NumberInterface
class NumberReducer(val abacus: Abacus, context: EvaluationContext) : Reducer<NumberInterface> {
val context = context.mutableSubInstance()
init {
this.context.reducer = this
}
override fun reduceNode(treeNode: TreeNode, vararg children: Any): NumberInterface {
val promotionManager = abacus.promotionManager
return when(treeNode){
is NumberNode -> {
context.inheritedNumberImplementation?.instanceForString(treeNode.number)
?: throw EvaluationException("no number implementation selected.")
}
is VariableNode -> {
val variable = context.getVariable(treeNode.variable)
if(variable != null) return variable
val definition = context.getDefinition(treeNode.variable)
if(definition != null) return definition.reduce(this)
throw EvaluationException("variable is not defined.")
}
is NumberUnaryNode -> {
val child = children[0] as NumberInterface
context.numberImplementation = abacus.pluginManager.interfaceImplementationFor(child.javaClass)
abacus.pluginManager.operatorFor(treeNode.operation)
.apply(context, child)
}
is NumberBinaryNode -> {
val left = children[0] as NumberInterface
val right = children[1] as NumberInterface
val promotionResult = promotionManager.promote(left, right) ?:
throw EvaluationException("promotion failed.")
context.numberImplementation = promotionResult.promotedTo
abacus.pluginManager.operatorFor(treeNode.operation).apply(context, *promotionResult.items)
}
is FunctionNode -> {
val promotionResult = promotionManager
.promote(*children.map { it as NumberInterface }.toTypedArray()) ?:
throw EvaluationException("promotion failed.")
context.numberImplementation = promotionResult.promotedTo
abacus.pluginManager.functionFor(treeNode.callTo).apply(context, *promotionResult.items)
}
is TreeValueUnaryNode -> {
abacus.pluginManager.treeValueOperatorFor(treeNode.operation)
.apply(context, treeNode.applyTo)
}
is TreeValueBinaryNode -> {
abacus.pluginManager.treeValueOperatorFor(treeNode.operation)
.apply(context, treeNode.left, treeNode.right)
}
is TreeValueFunctionNode -> {
abacus.pluginManager.treeValueFunctionFor(treeNode.callTo)
.apply(context, *treeNode.children.toTypedArray())
}
else -> throw EvaluationException("unrecognized tree node.")
}
}
}

View File

@@ -1,5 +1,7 @@
package org.nwapw.abacus.tree package org.nwapw.abacus.tree
import org.nwapw.abacus.tree.nodes.TreeNode
/** /**
* Reducer interface that takes a tree and returns a single value. * Reducer interface that takes a tree and returns a single value.
* *

View File

@@ -1,4 +1,4 @@
package org.nwapw.abacus.tree package org.nwapw.abacus.tree.nodes
/** /**
* A tree node that holds a binary operation. * A tree node that holds a binary operation.

View File

@@ -1,4 +1,4 @@
package org.nwapw.abacus.tree package org.nwapw.abacus.tree.nodes
/** /**
* Represents a more generic function call. * Represents a more generic function call.

View File

@@ -1,4 +1,6 @@
package org.nwapw.abacus.tree package org.nwapw.abacus.tree.nodes
import org.nwapw.abacus.tree.Reducer
/** /**
* A binary operator node that reduces its children. * A binary operator node that reduces its children.

View File

@@ -1,4 +1,6 @@
package org.nwapw.abacus.tree package org.nwapw.abacus.tree.nodes
import org.nwapw.abacus.tree.Reducer
/** /**
* A tree node that holds a function call. * A tree node that holds a function call.
@@ -8,7 +10,7 @@ package org.nwapw.abacus.tree
* *
* @param function the function string. * @param function the function string.
*/ */
class FunctionNode(function: String, children: List<TreeNode>) : CallNode(function, children) { class NumberFunctionNode(function: String, children: List<TreeNode>) : CallNode(function, children) {
override fun <T : Any> reduce(reducer: Reducer<T>): T { override fun <T : Any> reduce(reducer: Reducer<T>): T {
val children = Array<Any>(children.size, { children[it].reduce(reducer) }) val children = Array<Any>(children.size, { children[it].reduce(reducer) })

View File

@@ -1,4 +1,6 @@
package org.nwapw.abacus.tree package org.nwapw.abacus.tree.nodes
import org.nwapw.abacus.tree.Reducer
/** /**
* A tree node that holds a single number value. * A tree node that holds a single number value.

View File

@@ -1,4 +1,6 @@
package org.nwapw.abacus.tree package org.nwapw.abacus.tree.nodes
import org.nwapw.abacus.tree.Reducer
/** /**
* A unary operator node that reduces its children. * A unary operator node that reduces its children.

View File

@@ -1,4 +1,6 @@
package org.nwapw.abacus.tree package org.nwapw.abacus.tree.nodes
import org.nwapw.abacus.tree.Reducer
/** /**
* A tree node. * A tree node.

View File

@@ -1,4 +1,6 @@
package org.nwapw.abacus.tree package org.nwapw.abacus.tree.nodes
import org.nwapw.abacus.tree.Reducer
/** /**
* A tree node that represents a binary tree value operator. * A tree node that represents a binary tree value operator.

View File

@@ -1,4 +1,6 @@
package org.nwapw.abacus.tree package org.nwapw.abacus.tree.nodes
import org.nwapw.abacus.tree.Reducer
/** /**
* A tree node that represents a tree value function call. * A tree node that represents a tree value function call.

View File

@@ -1,4 +1,6 @@
package org.nwapw.abacus.tree package org.nwapw.abacus.tree.nodes
import org.nwapw.abacus.tree.Reducer
/** /**
* A tree node that represents a unary tree value operator. * A tree node that represents a unary tree value operator.

View File

@@ -1,4 +1,4 @@
package org.nwapw.abacus.tree package org.nwapw.abacus.tree.nodes
/** /**
* A tree node that holds a unary operation. * A tree node that holds a unary operation.

View File

@@ -1,4 +1,6 @@
package org.nwapw.abacus.tree package org.nwapw.abacus.tree.nodes
import org.nwapw.abacus.tree.Reducer
/** /**
* A tree node that holds a placeholder variable. * A tree node that holds a placeholder variable.

View File

@@ -7,8 +7,8 @@ import org.nwapw.abacus.Abacus;
import org.nwapw.abacus.config.Configuration; import org.nwapw.abacus.config.Configuration;
import org.nwapw.abacus.exception.DomainException; import org.nwapw.abacus.exception.DomainException;
import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.number.NumberInterface;
import org.nwapw.abacus.plugin.StandardPlugin; import org.nwapw.abacus.plugin.standard.StandardPlugin;
import org.nwapw.abacus.tree.TreeNode; import org.nwapw.abacus.tree.nodes.TreeNode;
public class CalculationTests { public class CalculationTests {
@@ -22,7 +22,6 @@ public class CalculationTests {
private void testOutput(String input, String parseOutput, String output) { private void testOutput(String input, String parseOutput, String output) {
TreeNode parsedTree = abacus.parseString(input); TreeNode parsedTree = abacus.parseString(input);
Assert.assertNotNull(parsedTree);
Assert.assertEquals(parsedTree.toString(), parseOutput); Assert.assertEquals(parsedTree.toString(), parseOutput);
NumberInterface result = abacus.evaluateTree(parsedTree).getValue(); NumberInterface result = abacus.evaluateTree(parsedTree).getValue();
Assert.assertNotNull(result); Assert.assertNotNull(result);
@@ -31,7 +30,6 @@ public class CalculationTests {
private void testDomainException(String input, String parseOutput) { private void testDomainException(String input, String parseOutput) {
TreeNode parsedTree = abacus.parseString(input); TreeNode parsedTree = abacus.parseString(input);
Assert.assertNotNull(parsedTree);
Assert.assertEquals(parsedTree.toString(), parseOutput); Assert.assertEquals(parsedTree.toString(), parseOutput);
try { try {
abacus.evaluateTree(parsedTree); abacus.evaluateTree(parsedTree);

View File

@@ -3,7 +3,7 @@ package org.nwapw.abacus.tests;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.nwapw.abacus.lexing.Lexer; import org.nwapw.abacus.lexing.Lexer;
import org.nwapw.abacus.lexing.pattern.Match; import org.nwapw.abacus.lexing.Match;
import java.util.List; import java.util.List;

View File

@@ -0,0 +1,95 @@
package org.nwapw.abacus.tests;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.nwapw.abacus.Abacus;
import org.nwapw.abacus.config.Configuration;
import org.nwapw.abacus.number.promotion.PromotionFunction;
import org.nwapw.abacus.number.range.NumberRange;
import org.nwapw.abacus.number.standard.NaiveNumber;
import org.nwapw.abacus.number.standard.PreciseNumber;
import org.nwapw.abacus.plugin.standard.StandardPlugin;
public class RangeTests {
private static Abacus abacus = new Abacus(new Configuration( "precise", new String[]{}));
private static PromotionFunction naivePromotion = i -> new NaiveNumber((i.toString()));
private static PromotionFunction precisePromotion = i -> new PreciseNumber((i.toString()));
@BeforeClass
public static void prepareTests() {
abacus.getPluginManager().addInstantiated(new StandardPlugin(abacus.getPluginManager()));
abacus.reload();
}
public static NumberRange naiveRange(String bottom, String top) {
return new NaiveNumber(bottom).rangeTo(new NaiveNumber(top)).with(abacus);
}
@Test
public void testNaiveRange(){
NumberRange range = naiveRange("0", "10");
Assert.assertTrue(range.getStart().toString().startsWith("0"));
Assert.assertTrue(range.getEndInclusive().toString().startsWith("10"));
}
@Test
public void testNaiveRangeBelow() {
NumberRange range = naiveRange("0", "10");
Assert.assertFalse(range.contains(new NaiveNumber("-10")));
}
@Test
public void testNaiveRangeAbove() {
NumberRange range = naiveRange("0", "10");
Assert.assertFalse(range.contains(new NaiveNumber("20")));
}
@Test
public void testNaiveRangeJustWithinBottom() {
NumberRange range = naiveRange("0", "10");
Assert.assertTrue(range.contains(new NaiveNumber("0")));
}
@Test
public void testNaiveRangeJustWithinTop() {
NumberRange range = naiveRange("0", "10");
Assert.assertTrue(range.contains(new NaiveNumber("10")));
}
@Test
public void testNaiveRangeWithin() {
NumberRange range = naiveRange("0", "10");
Assert.assertTrue(range.contains(new NaiveNumber("5")));
}
public static void addTestPromotionPaths() {
StandardPlugin.IMPLEMENTATION_NAIVE.getPromotionPaths().put("precise", precisePromotion);
StandardPlugin.IMPLEMENTATION_PRECISE.getPromotionPaths().put("naive", naivePromotion);
abacus.reload();
}
public static void removeTestPromotionPaths() {
StandardPlugin.IMPLEMENTATION_NAIVE.getPromotionPaths().remove("precise");
StandardPlugin.IMPLEMENTATION_NAIVE.getPromotionPaths().remove("naive");
abacus.reload();
}
@Test
public void testPromotionWithin() {
addTestPromotionPaths();
NumberRange range = naiveRange("0", "10");
Assert.assertTrue(range.contains(new PreciseNumber("5")));
removeTestPromotionPaths();
}
@Test
public void testPromotionOutside(){
addTestPromotionPaths();
NumberRange range = naiveRange("0","10");
Assert.assertFalse(range.contains(new PreciseNumber("20")));
removeTestPromotionPaths();
}
}

View File

@@ -1,17 +1,22 @@
package org.nwapw.abacus.tests; package org.nwapw.abacus.tests;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert; import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.nwapw.abacus.Abacus; import org.nwapw.abacus.Abacus;
import org.nwapw.abacus.config.Configuration; import org.nwapw.abacus.config.Configuration;
import org.nwapw.abacus.context.MutableEvaluationContext; import org.nwapw.abacus.context.MutableEvaluationContext;
import org.nwapw.abacus.function.*; import org.nwapw.abacus.context.PluginEvaluationContext;
import org.nwapw.abacus.lexing.pattern.Match; import org.nwapw.abacus.function.OperatorAssociativity;
import org.nwapw.abacus.function.OperatorType;
import org.nwapw.abacus.function.interfaces.NumberFunction;
import org.nwapw.abacus.function.interfaces.NumberOperator;
import org.nwapw.abacus.lexing.Match;
import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.number.NumberInterface;
import org.nwapw.abacus.parsing.LexerTokenizer; import org.nwapw.abacus.parsing.standard.LexerTokenizer;
import org.nwapw.abacus.parsing.standard.TokenType;
import org.nwapw.abacus.plugin.Plugin; import org.nwapw.abacus.plugin.Plugin;
import org.nwapw.abacus.tree.TokenType;
import java.util.List; import java.util.List;
@@ -21,12 +26,12 @@ public class TokenizerTests {
private static LexerTokenizer lexerTokenizer = new LexerTokenizer(); private static LexerTokenizer lexerTokenizer = new LexerTokenizer();
private static NumberFunction subtractFunction = new NumberFunction() { private static NumberFunction subtractFunction = new NumberFunction() {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return params.length == 2; return params.length == 2;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
return params[0].subtract(params[1]); return params[0].subtract(params[1]);
} }
}; };
@@ -37,12 +42,12 @@ public class TokenizerTests {
0) { 0) {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return true; return true;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
return subtractFunction.apply(context, params); return subtractFunction.apply(context, params);
} }
}); });
@@ -50,12 +55,12 @@ public class TokenizerTests {
0) { 0) {
@Override @Override
public boolean matchesParams(MutableEvaluationContext context, NumberInterface[] params) { public boolean matchesParams(PluginEvaluationContext context, NumberInterface[] params) {
return true; return true;
} }
@Override @Override
public NumberInterface applyInternal(MutableEvaluationContext context, NumberInterface[] params) { public NumberInterface applyInternal(PluginEvaluationContext context, NumberInterface[] params) {
return subtractFunction.apply(context, params); return subtractFunction.apply(context, params);
} }
}); });

View File

@@ -1,7 +1,7 @@
apply plugin: 'application' apply plugin: 'application'
dependencies { dependencies {
compile 'com.moandjiezana.toml:toml4j:0.7.1' compile 'com.moandjiezana.toml:toml4j:0.7.2'
compile project(':core') compile project(':core')
} }

View File

@@ -26,7 +26,7 @@ public class AbacusApplication extends Application {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/abacus.fxml")); FXMLLoader loader = new FXMLLoader(getClass().getResource("/abacus.fxml"));
Parent parent = loader.load(); Parent parent = loader.load();
controller = loader.getController(); controller = loader.getController();
Scene mainScene = new Scene(parent, 320, 480); Scene mainScene = new Scene(parent, 420, 520);
primaryStage.setScene(mainScene); primaryStage.setScene(mainScene);
primaryStage.setTitle("Abacus"); primaryStage.setTitle("Abacus");
primaryStage.show(); primaryStage.show();

View File

@@ -8,28 +8,28 @@ import javafx.fxml.FXML;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.control.cell.CheckBoxListCell; import javafx.scene.control.cell.CheckBoxListCell;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import javafx.stage.FileChooser;
import javafx.util.Callback; import javafx.util.Callback;
import javafx.util.StringConverter; import javafx.util.StringConverter;
import org.nwapw.abacus.Abacus; import org.nwapw.abacus.Abacus;
import org.nwapw.abacus.config.Configuration; import org.nwapw.abacus.config.Configuration;
import org.nwapw.abacus.exception.AbacusException; import org.nwapw.abacus.exception.AbacusException;
import org.nwapw.abacus.exception.ComputationInterruptedException;
import org.nwapw.abacus.function.Documentation; import org.nwapw.abacus.function.Documentation;
import org.nwapw.abacus.function.DocumentationType; import org.nwapw.abacus.function.DocumentationType;
import org.nwapw.abacus.exception.DomainException;
import org.nwapw.abacus.exception.EvaluationException;
import org.nwapw.abacus.number.*; import org.nwapw.abacus.number.*;
import org.nwapw.abacus.plugin.ClassFinder;
import org.nwapw.abacus.plugin.PluginListener; import org.nwapw.abacus.plugin.PluginListener;
import org.nwapw.abacus.plugin.PluginManager; import org.nwapw.abacus.plugin.PluginManager;
import org.nwapw.abacus.plugin.StandardPlugin; import org.nwapw.abacus.plugin.standard.StandardPlugin;
import org.nwapw.abacus.tree.EvaluationResult; import org.nwapw.abacus.EvaluationResult;
import org.nwapw.abacus.tree.TreeNode; import org.nwapw.abacus.tree.nodes.TreeNode;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.Scanner;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -73,6 +73,7 @@ public class AbacusController implements PluginListener {
* Constant string that is displayed if the calculations are interrupted by an exception. * Constant string that is displayed if the calculations are interrupted by an exception.
*/ */
private static final String ERR_EXCEPTION = "Exception Thrown"; private static final String ERR_EXCEPTION = "Exception Thrown";
private static final String ERR_DEFINITION = "Definition Error";
@FXML @FXML
private TabPane coreTabPane; private TabPane coreTabPane;
@FXML @FXML
@@ -107,6 +108,8 @@ public class AbacusController implements PluginListener {
private ListView<Documentation> functionListView; private ListView<Documentation> functionListView;
@FXML @FXML
private TextField functionListSearchField; private TextField functionListSearchField;
@FXML
private ListView<String> definitionFilesView;
/** /**
* The list of history entries, created by the users. * The list of history entries, created by the users.
@@ -132,6 +135,10 @@ public class AbacusController implements PluginListener {
* The filtered list displayed to the user. * The filtered list displayed to the user.
*/ */
private FilteredList<Documentation> functionFilter; private FilteredList<Documentation> functionFilter;
/**
* The list of definition files to be loaded.
*/
private ObservableList<String> definitionFiles;
/** /**
* The abacus instance used for changing the plugin configuration. * The abacus instance used for changing the plugin configuration.
@@ -145,9 +152,6 @@ public class AbacusController implements PluginListener {
private String attemptCalculation() { private String attemptCalculation() {
try { try {
TreeNode constructedTree = abacus.parseString(inputField.getText()); TreeNode constructedTree = abacus.parseString(inputField.getText());
if (constructedTree == null) {
return ERR_SYNTAX;
}
EvaluationResult result = abacus.evaluateTree(constructedTree); EvaluationResult result = abacus.evaluateTree(constructedTree);
NumberInterface evaluatedNumber = result.getValue(); NumberInterface evaluatedNumber = result.getValue();
String resultingString = evaluatedNumber.toString(); String resultingString = evaluatedNumber.toString();
@@ -258,6 +262,9 @@ public class AbacusController implements PluginListener {
if (oldValue.equals(settingsTab)) alertIfApplyNeeded(true); if (oldValue.equals(settingsTab)) alertIfApplyNeeded(true);
}); });
definitionFiles = FXCollections.observableArrayList();
definitionFilesView.setItems(definitionFiles);
abacus = new Abacus(new ExtendedConfiguration(CONFIG_FILE)); abacus = new Abacus(new ExtendedConfiguration(CONFIG_FILE));
PluginManager abacusPluginManager = abacus.getPluginManager(); PluginManager abacusPluginManager = abacus.getPluginManager();
abacusPluginManager.addListener(this); abacusPluginManager.addListener(this);
@@ -311,6 +318,25 @@ public class AbacusController implements PluginListener {
reloadAlertShown = false; reloadAlertShown = false;
} }
private void loadDefinitionFile(String fileName){
File definitionFile = new File(fileName);
if(!definitionFile.exists()) return;
try {
FileReader fileReader = new FileReader(definitionFile);
Scanner scanner = new Scanner(fileReader);
while(scanner.hasNext()){
EvaluationResult result = abacus.evaluateTree(abacus.parseString(scanner.nextLine()));
abacus.applyToContext(result.getResultingContext());
}
} catch (AbacusException abacusError) {
outputText.setText(ERR_DEFINITION + " (" + abacusError.getMessage() + ")");
abacusError.printStackTrace();
} catch (RuntimeException runtime) {
outputText.setText(ERR_DEFINITION + " (" + ERR_EXCEPTION + ")");
runtime.printStackTrace();
} catch (FileNotFoundException ignored) {}
}
@FXML @FXML
public void performScan() { public void performScan() {
PluginManager abacusPluginManager = abacus.getPluginManager(); PluginManager abacusPluginManager = abacus.getPluginManager();
@@ -321,34 +347,62 @@ public class AbacusController implements PluginListener {
} catch (IOException | ClassNotFoundException e) { } catch (IOException | ClassNotFoundException e) {
e.printStackTrace(); e.printStackTrace();
} }
abacus.reload(); reloadAbacus();
} }
@FXML @FXML
public void performReload() { public void performReload() {
alertIfApplyNeeded(true); alertIfApplyNeeded(true);
abacus.reload(); reloadAbacus();
} }
@FXML @FXML
public void performSave() { public void performSave() {
Configuration configuration = abacus.getConfiguration(); ExtendedConfiguration configuration = (ExtendedConfiguration) abacus.getConfiguration();
configuration.setNumberImplementation(numberImplementationBox.getSelectionModel().getSelectedItem()); configuration.setNumberImplementation(numberImplementationBox.getSelectionModel().getSelectedItem());
Set<String> disabledPlugins = configuration.getDisabledPlugins(); Set<String> disabledPlugins = configuration.getDisabledPlugins();
disabledPlugins.clear(); disabledPlugins.clear();
for (ToggleablePlugin pluginEntry : enabledPlugins) { for (ToggleablePlugin pluginEntry : enabledPlugins) {
if (!pluginEntry.isEnabled()) disabledPlugins.add(pluginEntry.getClassName()); if (!pluginEntry.isEnabled()) disabledPlugins.add(pluginEntry.getClassName());
} }
Set<String> abacusDefinitionFiles = configuration.getDefinitionFiles();
abacusDefinitionFiles.clear();
abacusDefinitionFiles.addAll(definitionFiles);
if (computationLimitField.getText().matches("\\d*(\\.\\d+)?") && computationLimitField.getText().length() != 0) if (computationLimitField.getText().matches("\\d*(\\.\\d+)?") && computationLimitField.getText().length() != 0)
((ExtendedConfiguration) configuration).setComputationDelay(Double.parseDouble(computationLimitField.getText())); configuration.setComputationDelay(Double.parseDouble(computationLimitField.getText()));
((ExtendedConfiguration) configuration).saveTo(CONFIG_FILE); configuration.saveTo(CONFIG_FILE);
changesMade = false; changesMade = false;
reloadAlertShown = false; reloadAlertShown = false;
} }
@FXML
public void performAddDefinitionFile(){
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Add definition file");
File selectedFile = fileChooser.showOpenDialog(null);
if(selectedFile == null) return;
String absolutePath = selectedFile.getAbsolutePath();
if(!definitionFiles.contains(absolutePath)) definitionFiles.add(absolutePath);
changesMade = true;
}
@FXML
public void performRemoveDefinitionFile(){
String selectedItem = definitionFilesView.getSelectionModel().getSelectedItem();
if(selectedItem != null) definitionFiles.remove(selectedItem);
changesMade = true;
}
private void reloadAbacus(){
abacus.reload();
for(String file : definitionFiles){
loadDefinitionFile(file);
}
}
@Override @Override
public void onLoad(PluginManager manager) { public void onLoad(PluginManager manager) {
Configuration configuration = abacus.getConfiguration(); ExtendedConfiguration configuration = (ExtendedConfiguration) abacus.getConfiguration();
Set<String> disabledPlugins = configuration.getDisabledPlugins(); Set<String> disabledPlugins = configuration.getDisabledPlugins();
numberImplementationOptions.addAll(abacus.getPluginManager().getAllNumberImplementations()); numberImplementationOptions.addAll(abacus.getPluginManager().getAllNumberImplementations());
String actualImplementation = configuration.getNumberImplementation(); String actualImplementation = configuration.getNumberImplementation();
@@ -361,11 +415,20 @@ public class AbacusController implements PluginListener {
enabledPlugins.add(plugin); enabledPlugins.add(plugin);
} }
PluginManager pluginManager = abacus.getPluginManager(); PluginManager pluginManager = abacus.getPluginManager();
functionList.addAll(manager.getAllFunctions().stream().map(name -> pluginManager.documentationFor(name, DocumentationType.FUNCTION)) functionList.addAll(manager.getAllFunctions().stream().map(name -> {
.collect(Collectors.toCollection(ArrayList::new))); Documentation documentationInstance = pluginManager.documentationFor(name, DocumentationType.FUNCTION);
functionList.addAll(manager.getAllTreeValueFunctions().stream().map(name -> pluginManager.documentationFor(name, DocumentationType.TREE_VALUE_FUNCTION)) if(documentationInstance == null)
.collect(Collectors.toCollection(ArrayList::new))); documentationInstance = new Documentation(name, "", "", "", DocumentationType.FUNCTION);
return documentationInstance;
}).collect(Collectors.toCollection(ArrayList::new)));
functionList.addAll(manager.getAllTreeValueFunctions().stream().map(name -> {
Documentation documentationInstance = pluginManager.documentationFor(name, DocumentationType.TREE_VALUE_FUNCTION);
if(documentationInstance == null)
documentationInstance = new Documentation(name, "", "", "", DocumentationType.TREE_VALUE_FUNCTION);
return documentationInstance;
}).collect(Collectors.toCollection(ArrayList::new)));
functionList.sort(Comparator.comparing(Documentation::getCodeName)); functionList.sort(Comparator.comparing(Documentation::getCodeName));
definitionFiles.addAll(configuration.getDefinitionFiles());
} }
@Override @Override
@@ -373,6 +436,7 @@ public class AbacusController implements PluginListener {
functionList.clear(); functionList.clear();
enabledPlugins.clear(); enabledPlugins.clear();
numberImplementationOptions.clear(); numberImplementationOptions.clear();
definitionFiles.clear();
} }
} }

View File

@@ -1,4 +1,4 @@
package org.nwapw.abacus.plugin; package org.nwapw.abacus.fx;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;

View File

@@ -17,6 +17,7 @@ import java.io.File
* @param disabledPlugins the list of plugins that should be disabled, same as [Configuration.disabledPlugins] * @param disabledPlugins the list of plugins that should be disabled, same as [Configuration.disabledPlugins]
*/ */
class ExtendedConfiguration(var computationDelay: Double = 0.0, class ExtendedConfiguration(var computationDelay: Double = 0.0,
definitionFiles: Array<String> = emptyArray(),
implementation: String = "<default>", implementation: String = "<default>",
disabledPlugins: Array<String> = emptyArray()) disabledPlugins: Array<String> = emptyArray())
: Configuration(implementation, disabledPlugins) { : Configuration(implementation, disabledPlugins) {
@@ -27,6 +28,7 @@ class ExtendedConfiguration(var computationDelay: Double = 0.0,
*/ */
val DEFAULT_TOML_STRING = """ val DEFAULT_TOML_STRING = """
computationDelay=0.0 computationDelay=0.0
definitionFiles=[]
implementation="naive" implementation="naive"
disabledPlugins=[] disabledPlugins=[]
""" """
@@ -40,6 +42,11 @@ class ExtendedConfiguration(var computationDelay: Double = 0.0,
val DEFAULT_TOML_WRITER = TomlWriter() val DEFAULT_TOML_WRITER = TomlWriter()
} }
/**
* The set of files that definitions should be loaded from.
*/
val definitionFiles: MutableSet<String> = mutableSetOf(*definitionFiles)
/** /**
* Constructs a new configuration from a file on disk. * Constructs a new configuration from a file on disk.
* @param tomlFile the file from disk to load. * @param tomlFile the file from disk to load.
@@ -59,6 +66,8 @@ class ExtendedConfiguration(var computationDelay: Double = 0.0,
numberImplementation = config.numberImplementation numberImplementation = config.numberImplementation
disabledPlugins.clear() disabledPlugins.clear()
disabledPlugins.addAll(config.disabledPlugins) disabledPlugins.addAll(config.disabledPlugins)
definitionFiles.clear()
definitionFiles.addAll(config.definitionFiles)
} }
/** /**

View File

@@ -47,17 +47,33 @@
</padding> </padding>
<Label text="Number Implementation" GridPane.columnIndex="0" GridPane.rowIndex="0"/> <Label text="Number Implementation" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
<ComboBox fx:id="numberImplementationBox" GridPane.columnIndex="1" GridPane.rowIndex="0"/> <ComboBox fx:id="numberImplementationBox" GridPane.columnIndex="1" GridPane.rowIndex="0"/>
<Label text="Plugins:" GridPane.columnIndex="0" GridPane.rowIndex="1"/>
<ListView fx:id="enabledPluginView" <ListView fx:id="enabledPluginView"
GridPane.rowIndex="1" GridPane.columnIndex="0" GridPane.rowIndex="2" GridPane.columnIndex="0"
GridPane.columnSpan="2" maxHeight="100"/> GridPane.columnSpan="2" maxHeight="100"/>
<Text GridPane.columnIndex="0" GridPane.rowIndex="2" text="Computation Limit"/>
<TextField fx:id="computationLimitField" GridPane.columnIndex="1" GridPane.rowIndex="2"/> <Label GridPane.columnIndex="0" GridPane.rowIndex="3" text="Computation Limit"/>
<FlowPane GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="3" hgap="10" <TextField fx:id="computationLimitField" GridPane.columnIndex="1" GridPane.rowIndex="3"/>
<Label text="Definition Files:" GridPane.columnIndex="0" GridPane.rowIndex="4"/>
<ListView fx:id="definitionFilesView"
GridPane.columnIndex="0" GridPane.columnSpan="2"
GridPane.rowIndex="5" maxHeight="100"/>
<FlowPane GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="6" hgap="10"
vgap="10"> vgap="10">
<Button text="Save" onAction="#performSave"/> <Button text="Add" onAction="#performAddDefinitionFile"/>
<Button text="Reload Plugins" onAction="#performReload"/> <Button text="Remove" onAction="#performRemoveDefinitionFile"/>
<Button text="Apply and Reload" onAction="#performSaveAndReload"/> </FlowPane>
<Button text="Scan Plugins" onAction="#performScan"/>
<Separator GridPane.rowIndex="7"/>
<FlowPane GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="8" hgap="10"
vgap="10">
<Button text="Save Settings" onAction="#performSave"/>
<Button text="Reload Plugins And Definitions" onAction="#performReload"/>
<Button text="Save and Reload" onAction="#performSaveAndReload"/>
<Button text="Reload Plugins From Disk" onAction="#performScan"/>
</FlowPane> </FlowPane>
</GridPane> </GridPane>
</Tab> </Tab>

Binary file not shown.

View File

@@ -1,6 +1,5 @@
#Fri Jul 28 17:18:51 PDT 2017
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-bin.zip

6
gradlew vendored
View File

@@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD="maximum"
warn ( ) { warn () {
echo "$*" echo "$*"
} }
die ( ) { die () {
echo echo
echo "$*" echo "$*"
echo echo
@@ -155,7 +155,7 @@ if $cygwin ; then
fi fi
# Escape application args # Escape application args
save ( ) { save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " " echo " "
} }