This lesson is mainly about parsing user input, where the user is the operator or user of the application. Here’s an example of what we’re aiming for:
- From the console, the operator enters a simple equation…
y=2x^2 + 3x - 1 - … a range and an increment…
start -2
end 2
step .01 - … and then asks us to plot the equation over the given range:
yplot
Here’s another example, in which the operator asks us to plot a parametric equation over a given range:
start 0
end 2pi
step .005
set a=3,n=4
x=a*sin(nt)cos(t)
y=a*sin(nt)sin(t)
xyplot
The above examples assume operator input comes from a console, but in fact, it could come from a GUI, a text file, a website, or any other textual source. For the more difficult part of the task, parsing mathematical expressions, we’ll use a third-party library.
In this lesson, we’ll look at the exp4j API for parsing expressions, and then design the facilities we’ll need to achieve our goals. Implementation of the facilities will take place in the next lesson.
GitHub repository: Cartesian Plane Part 9
Previous lesson: Cartesian Plane Lesson 8: Streaming
Introduction to Exp4j
This lesson is about parsing user input, but that entails two different tasks. One is fairly straightforward; an example would be parsing the following input into a command (CHANGE_COLOR) and argument (blue):
CHANGE_COLOR blue
Another example (that I, at least, consider straightforward) would be to parse a string of comma-separated strings into name/value pairs:
a=5,b=10,c=42
A far more difficult task is parsing a mathematical equation such as this one:
a + cos(b * t) * b + sin(a * t)
Parsing expressions such as the above entails accounting for functions and function arguments, operator precedence and associativity, mapping variables to values, and other considerations. Such a task is worthy of an advanced computer science project. So we’re not going to do that. Instead, we are going to adopt an existing library for parsing expressions, exp4j. To use this library, you must download it and add it to your classpath. There are two ways to do this:
- Using Maven: add the following dependency to your pom.xml file:
<dependency>
In Eclipse, refresh your project (file → refresh), make sure project → Build Automatically is checked, and select project → clean.
<groupId>net.objecthunter</groupId>
<artifactId>exp4j</artifactId>
<version>0.4.8</version>
</dependency> - Direct download: first, create a new directory for storing libraries; commonly, this is named lib. Choose a location for this directory carefully; one logical place is the directory above your project root. Another logical location is your home directory. Now download the exp4j jar file into this directory; the download link is currently https://www.objecthunter.net/exp4j/download.html. To add the jar file to your classpath, go to Eclipse, right-click on your project name, and select Build Path → Configure Build Path. On the libraries tab, select Classpath, and push the Add External JARs button. Locate and select the exp4j jar file.

Here are some helpful exp4j links:
- Download: https://www.objecthunter.net/exp4j/download.html
- Introduction: https://www.objecthunter.net/exp4j/
- Javadoc: https://www.objecthunter.net/exp4j/apidocs/index.html
Exp4j Expression Syntax
y=2x^2 + 3x - 1
All numeric values in exp4j are treated as type double. Most Java arithmetic expressions can be translated directly into exp4j syntax, for example, (x + 4)/(x + 3). Note that, in this example, exp4j will treat 4 and 3 as type double. In addition, exp4j includes an extra operator to denote exponentiation, the caret (^); the following would be read “a times x squared + 4”: a * x^2 + 4.
Exp4j variable names follow the same rules as Java; names must start with a letter or underscore, and are case-sensitive: leg_4x / leg_3x.
Exp4j understands implicit operations you’ll recognize from math class, such as:
- If a and b are variable names, ab is equivalent to a * b.
- 4x^2 is equivalent to 4 * x^2.
- (x + 1)(x + 2) is equivalent to (x + 1) *(x + 2).
Caveat: when using implicit operations, beware of ambiguous expressions. For example, this expression:
asin(x)
can be interpreted as “a times the sine of x,” or “the arcsine of x”; the operator needs to be made aware of this. The ambiguity can be resolved by inserting whitespace between “a” and “sin,” or by making the operation explicit:
a sin(x)
a*sin(x)
Exp4j recognizes the constants pi, π, φ (the golden ratio), and e.
Exp4j has built-in functions for the most common mathematical operations (for a complete list, see Built-in Functions on the exp4j website); use them just like you would in a Java expression:
cos(pi)cosine of pisin(pi/2)sine of (pi divided by 2)acos(x*4)arc cosine of (x times 4)
You can write your own custom functions and custom operators.
Exp4j API
All examples are in the sandbox package of this project
For our purposes, the three main classes in the exp4j library are Expression, ExpressionBuilder, and ValidationResult. Expression is the workhorse, but you need ExpressionBuilder to create an Expression. A ValidationResult object describes whether or not an expression is valid, and, if it is not, a list of reasons. The steps in parsing and evaluating an expression are:
- Feed an expression to the ExpressionBuilder constructor to obtain an ExpressionBuilder object.
- Inform the ExpressionBuilder object of the names of your variables (if you have custom functions and/or operators, you have to inform the object of those, too).
- Use the ExpressionBuilder object to obtain an Expression.
- Inform the Expression object of the values of all your variables.
- Using the Expression object, evaluate the expression.
Here are some very, very simple examples.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class Exp4jDemo1SimpleExamples { public static void main(String[] args) { ExpressionBuilder bldr = null; Expression expr = null; double result = 0; bldr = new ExpressionBuilder( "4" ); expr = bldr.build(); result = expr.evaluate(); System.out.println( result ); bldr = new ExpressionBuilder( "2^5 + 7" ); expr = bldr.build(); result = expr.evaluate(); System.out.println( result ); bldr = new ExpressionBuilder( "(2 + 5)(3 - 7)" ); expr = bldr.build(); result = expr.evaluate(); System.out.println( result ); } } |
To identify variable names for ExpressionBuilder, use the variable method or one of the variables overloads. To assign a value to a variable when evaluating the expression with the Expression object, use the setVariable or setVariables method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class Exp4jDemo2UsingVariables { public static void main(String[] args) { ExpressionBuilder bldr = null; Expression expr = null; double result = 0; bldr = new ExpressionBuilder( "ax^2 + bx + c" ); bldr.variables( "a", "b", "c", "x" ); expr = bldr.build(); expr.setVariable( "a", 5 ); expr.setVariable( "b", -1 ); expr.setVariable( "c", -2 ); expr.setVariable( "x", .5 ); result = expr.evaluate(); System.out.println( result ); } } |
Many of the Expression and ExpressionBuilder methods return this, making method chaining convenient.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class Exp4jDemo3WithMethodChaining { public static void main(String[] args) { double result = 0; Expression expr = new ExpressionBuilder( "ax^2 + bx + c" ) .variables( "a", "b", "c", "x" ) .build(); result = expr.setVariable( "a", 5 ) .setVariable( "b", -1 ) .setVariable( "c", -2 ) .setVariable( "x", .5 ) .evaluate(); System.out.println( result ); } } |
You can use collections (mainly maps and sets) to manage variables.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class Exp4jDemo4UsingCollections { public static void main(String[] args) { Map<String,Double> vars = new HashMap<>(); double result = 0; vars.put( "a", 5. ); vars.put( "b", -1. ); vars.put( "c", -2. ); vars.put( "x", 0. ); Expression expr = new ExpressionBuilder( "ax^2 + bx + c" ) .variables( vars.keySet() ) .build(); result = expr.setVariables( vars ) .setVariable( "x", .5 ) .evaluate(); System.out.println( result ); } } |
Here is an example that generates a stream of (x, y) coordinates over a range of x values.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public class Exp4jDemo5StreamingCoordinates { public static void main(String[] args) { Map<String,Double> vars = new HashMap<>(); vars.put( "a", 5. ); vars.put( "b", -1. ); vars.put( "c", -2. ); vars.put( "x", 0. ); Expression expr = new ExpressionBuilder( "ax^2 + bx + c" ) .variables( vars.keySet() ) .build(); expr.setVariables( vars ); DoubleStream.iterate( -1, d -> d <= 1, d -> d + .01 ) .mapToObj( d -> { expr.setVariable( "x", d ); return new Point2D.Double( d, expr.evaluate() ); }) .forEach( System.out::println ); } } |
Bringing our lesson back to the Cartesian plane, here is a program to plot a quadratic function using our CartesianPlane class; the example is followed by some notes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | public class Exp4jDemo6PlottingPoints { private static final CartesianPlane plane = new CartesianPlane(); public static void main(String[] args) { Root root = new Root( plane ); root.start(); Map<String,Double> vars = new HashMap<>(); vars.put( "a", 5. ); vars.put( "b", -1. ); vars.put( "c", -2. ); vars.put( "x", 0. ); Expression expr = new ExpressionBuilder( "ax^2 + bx + c" ) .variables( vars.keySet() ) .build(); ToPlotPointCommand toPlotPointCommand = FIUtils.toPlotPointCommand( plane ); expr.setVariables( vars ); plane.setStreamSupplier( () -> DoubleStream.iterate( -1, d -> d <= 1, d -> d + .005 ) .mapToObj( d -> { expr.setVariable( "x", d ); return new Point2D.Double( d, expr.evaluate() ); }) .map( toPlotPointCommand::of ) ); NotificationManager.INSTANCE .propagateNotification( CPConstants.REDRAW_NP ); } } |
Notes:

- Lines 10 – 15: Creates a map of the variable names and their values to be used in the expression at line 17.
- Line 18: Creates an ExpressionBuilder to parse the given expression.
- Line 19: Tells the ExpressionBuilder the names of the variables we are using.
- Line 20: Instantiates the Expression object that encapsulates the parsed expression given at line 17.
- Lines 22-23: We first saw this logic in the last lesson; ToPlotPointCommand is a class with a method (of) that takes a Cartesian coordinate pair, and uses it to create a PlotPointCommand object.
- Line 26: Invokes the setStreamSupplier method in the CartesianPlane class. The argument is a functional interface implemented as a lambda; it begins with “() ->” and continues through line 30.
- Line 27: Generates a stream of doubles beginning at -1, and continuing to 1 in increments of .005.
- Line 28-31: Sets the value of x to the most recently generated double value; maps a value from the stream to an (x,y) coordinate pair, where the x-value is x, and the y-value is obtained by evaluating the exp4j Expression.
- Line 32: Maps the coordinate pair generated at line 29 to a PlotPointCommand.
- Lines 34,35: Generates a notification indicating that the plot has been updated, and the CartesianPlane object needs to be redrawn.
Let’s do one more example. This example uses two exp4j expressions to plot a parametric equation. We’ll use the equation for a Rose Curve, which has the properties:
x = a*sin(nt)cos(t)
y = a*sin(nt)sin(t)
where: n is an integer, t is an angle in radians
Caveat: I originally formulated the expressions using asin(nt), but exp4j interpreted that as “arcsine of nt” instead of “a times sine of nt.”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | public class Exp4jDemo7ParametricEquation { private static final CartesianPlane plane = new CartesianPlane(); public static void main(String[] args) { Root root = new Root( plane ); root.start(); Map<String,Double> vars = new HashMap<>(); vars.put( "a", 3. ); vars.put( "n", 4. ); vars.put( "t", 0. ); Expression roseExprX = new ExpressionBuilder( "a sin(nt)cos(t)" ) .variables( vars.keySet() ) .build(); Expression roseExprY = new ExpressionBuilder( "a sin(nt)sin(t)" ) .variables( vars.keySet() ) .build(); ToPlotPointCommand toPlotPointCommand = FIUtils.toPlotPointCommand( plane ); roseExprX.setVariables( vars ); roseExprY.setVariables( vars ); plane.setStreamSupplier( () -> DoubleStream.iterate( 0, t -> t < 2 * Math.PI, t -> t + .001 ) .mapToObj( t -> { roseExprX.setVariable( "t", t ); roseExprY.setVariable( "t", t ); return new Point2D.Double( roseExprX.evaluate(), roseExprY.evaluate() ); }) .map( toPlotPointCommand::of ) ); NotificationManager.INSTANCE .propagateNotification( CPConstants.REDRAW_NP ); } } |

Notes:
- Lines 10 – 14: Establishes a map of variable names to values.
- Line 16: Declares an exp4j Expression to encapsulate the formula for calculating the x-value.
- Line 17: Creates an ExpressionBuilder to parse the formula for calculating the x-value.
- Line 18: Tells the ExpressionBuilder what variable names we’re using.
- Line 19: Parses the encapsulated formula and creates an Expression object.
- Lines 21 – 24: Creates an Expression to encapsulate the formula for calculating the y-value.
- Lines 26 – 27: Creates an object that can map a Point2D to a PlotPointCommand.
- Line 31: Invokes the setStreamSupplier method in the CartesianPlane class. The argument is a functional interface implemented as a lambda; it begins with “() ->” and continues through line 41.
- Line 32: Generates a stream of double values in the range 0 to 2π, incrementing by .001 between iterations.
- Line 33-39: Generates x- and y-values, and stores them in a Point2D:
- Line 34: Sets the value of “t” (the parameter) in the expression to calculate the x-value.
- Line 35: Sets the value of “t” in the expression to calculate the y-value.
- Lines 36-39: Instantiates the Point2D.
- Line 41: Maps each Point2D object to a PlotPointCommand.
- Lines 43,44: Tells the CartesianPlane object to redraw itself.
Exp4j Custom Functions
Let’s have a quick discussion about how to write your own functions for exp4j. We’ll start with an example, followed by some notes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public class CustomFunctionDemo1ToRadians { public static void main(String[] args) { Function radians = new ToRadians(); Expression expr = new ExpressionBuilder( "toRadians( d )" ) .variables("d") .function( radians ) .build(); expr.setVariable( "d", 180 ); System.out.println( expr.evaluate() ); } private static class ToRadians extends Function { public ToRadians() { super( "toRadians", 1 ); } @Override public double apply( double... args ) { double radians = args[0] * Math.PI / 180.; return radians; } } } |
Notes:
- Lines 16 – 28: A custom function is implemented via a Java class that extends the exp4j Function class. Function is an abstract class, so it cannot be directly instantiated. Note also that it does not have a default constructor.
- Lines 18 – 21: Since Function doesn’t have a default constructor, our subclass must have a constructor that invokes the constructor in the superclass.
- Line 20: The Function constructor establishes the name of the function (the one that will be used in the expression string) and the number of arguments required by the function.
- Lines 23 – 28: The Function class has one abstract method, double apply( double… args ), which does the work of evaluating the function. Note that this is a variable-length argument (varargs) method, so its argument can be accessed as an array.
- Line 5: Variable radians is an instance of our Function subclass.
- Line 7: The expression “toRadians( d )” is parsed by ExpressionBuilder. Compare this with line 20, where we established that toRadians is the name of a function with one argument.
- Line 8: The variable d from the expression string is declared to the ExpressionBuilder.
- Line 9: Our custom function is declared to the ExpressionBuilder.
- Line 10: The Expression object is instantiated.
- Line 12: Within the Expression object, the variable d is given a value of 180.
- Line 13: The expression is evaluated, and the result is printed.
As a programmer familiar with anonymous classes and functional interfaces, you probably would not implement the above custom function using an explicit subclass of Function. You are more likely to use an anonymous class declaration/instantiation, such as this one:
Function radians = new Function( "toRadians", 1 ) {
@Override
public double apply( double... args ) {
return args[0] * Math.PI / 180;
}
};
Here’s an example that declares two custom functions using anonymous classes. Another difference between this example and the previous one is that we use the ExpressionBuilder method, functions(List<Function>), to declare multiple functions in a single method invocation. We could also have used two separate calls to the function(Function) method:
.function( degrees )
.function( radians )
or one invocation of the varargs overload, functions( Function… functions ):
.functions( degrees, radians )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | public class CustomFunctionDemo2DegreesRadians { public static void main(String[] args) { Function radians = new Function( "toRadians", 1 ) { @Override public double apply( double... args ) { return args[0] * Math.PI / 180; } }; Function degrees = new Function( "toDegrees", 1 ) { @Override public double apply( double... args ) { return args[0] * 180 / Math.PI; } }; List<Function> functionList = new ArrayList<>(); functionList.add( degrees ); functionList.add( radians ); Expression dToRExpr = new ExpressionBuilder( "toRadians( d )" ) .variables("d") .functions( functionList ) .build(); dToRExpr.setVariable( "d", 180 ); System.out.println( dToRExpr.evaluate() ); Expression rToDExpr = new ExpressionBuilder( "toDegrees( r )" ) .variables("r") .functions( functionList ) .build(); rToDExpr.setVariable( "r", Math.PI ); System.out.println( rToDExpr.evaluate() ); } } |
We’ll finish up with one more example. This one uses an anonymous class to define the toDegrees custom function, then uses it in a loop (implemented as a Stream) to calculate a series of values.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class CustomFunctionDemo3DegreesRadiansStream { public static void main(String[] args) { Function degrees = new Function( "toDegrees", 1 ) { @Override public double apply( double... args ) { return args[0] * 180 / Math.PI; } }; Expression dToRExpr = new ExpressionBuilder( "toDegrees( r )" ) .variables("r") .function( degrees ) .build(); Stream.of( 0., Math.PI / 2, Math.PI, 3 * Math.PI / 2, 2 * Math.PI ) .map( r -> { dToRExpr.setVariable( "r", r ); return dToRExpr.evaluate(); }) .forEach( System.out::println ); } } |
A Quick Word about Custom Operators
For the sake of completeness, I wanted to mention that the exp4j API allows you to write your own custom operators. This topic, however, has little relevance to our project. To learn more about custom operators, consult the exp4j documentation.
Obtaining Operator Input
Now that we know how to parse an expression, how are we going to get the expression to parse? The expression itself will have to be text, presumably supplied by a (non-programmer) human being. As we have seen, in addition to obtaining the expression itself, we will need the human to enter some configuration information, such as declaring variables and determining whether we’re plotting a simple function or a parametric equation. I suggest we should be prepared to gather input from any textual source, including command lines, GUIs, and files.
Suppose we are entering data from the command line, and each line starts with a command, optionally followed by an argument. Here’s what a typical dialog might look like:
enter command> set a=5,b=1,c=-1,x
enter command> yequals ax^2 + bx + c
enter command> yplot
In the above example:
- set is a command followed by an argument (we treat “a=5,b=1,c=-1,x” as one argument)
- yequals is a command followed by one argument
- yplot is a command with no arguments
The next question is: what, in general, does a line of input look like? I suggest that we should allow five different types of input:
- Empty lines, which are ignored.
- Lines beginning with ‘#’ are recognized as comments and ignored.
- Lines containing just a command.
- Lines containing a command followed by one or more arguments, separated by commas.
- Anything else, which will be treated as invalid.
We also have to be prepared for “end-of-input,” such as when we’ve read every line from a file.
Now, what sorts of things ought the input to be able to control? Here’s my list:
- Declare variables, optionally with initial values.
- Declare an iteration range for creating a plot: start value, end value, and increment value.
- Designate an expression that calculates a y-coordinate.
- For parametric equations, designate an expression that calculates an x-coordinate.
- For parametric equations, designate the name of the parameter.
- Plot an equation, y = f(x)
- Plot a parametric equation, (x,y) = f(t)
- Clear all settings and start fresh.
For the record: when we get around to writing the actual code, I will consider a few more context-dependent commands such as exit, open-file, and save-file.
Getting back to parsing, I see the need for two distinct utilities:
- A utility that parses input lines of the form command argument, which discards empty lines, comments, and excess whitespace, and parses a line into its constituent parts (command and argument). For a couple of reasons, I don’t want this facility to attempt to parse the argument or interpret the argument in the context of the command:
- In our sample scenarios (see bullet list above), there is at least one in which the argument must be parsed independently of the command. For example, the operator enters an argument into a text box in a GUI, then selects a button designating the desired command.
- Encapsulation! This is a great way to break down a complex operation into simpler operations that can be coded and debugged independently.
- A utility that interprets a command, parsing the argument (if any) in the context of that command.
In addition, we’ll need a facility to encapsulate all the resources needed to describe and plot an equation, including variable declarations, expressions for calculating x- and y-values, and an iteration range for generating the plot. We’ll plan to support simple functions (y = f (x)) and parametric equations ((x,y) = f(t)).
Let’s design a module for implementing the above facilities. Call it the input module.
The Input Module
This module is responsible for obtaining and parsing input lines and interpreting them in the context of configuring and plotting equations. Here are the types that the module will contain; they’ll all live in the com.acmemail.judah.cartesian_plane.input package.
enum Command
This enum will declare constants for all the different types of commands that might be found on a line of input. Each constant will declare a brief description for the purpose of generating user hints and documentation. We will start with these:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | public enum Command { EQUATION( "Creates a new equation" ), XEQUALS( "Describes any well-formed expression for the evaluation of \"x\" " + "in the coordinate pair \"(x,y)\"" ), YEQUALS( "Describes any well-formed expression for the evaluation of \"y\" " + "in the coordinate pair \"(x,y)\"" ), SET( "Describes a comma-separated list of " + "variables of the form name[=expression]" ), START( "Expression that describes the start value " + "of the iteration range" ), END( "Expression that describes the end value in the iteration range" ), STEP( "Expression that describes the increment value " + "for traversing the iteration range" ), PARAM( "Describes the name of the parameter in a parametric equation" ), YPLOT( "Generates a plot of the form y = f(x)" ), XYPLOT( "Generates a plot of the form (x,y) = f(t)" ), NONE( "Identifies an empty command string" ), INVALID( "Designates an invalid command." ), EXIT( "Application specific; probably " + "\"Exit from the current operation\"" ), OPEN( "Application specific; probably \"open equation file\""), SAVE( "Application specific; probably \"save equation file\""); private static final String lineSep = System.lineSeparator(); private final String desc; // ... } |
In addition, the enum will have the following public utilities:
public String getDescription(): return the brief description associated with this command.
public static Command toCommand( String from ): given a string, return the corresponding command; case-insensitive. Returns INVALID if the command is unrecognized.
public static String usage(): return a list of all commands and their associated brief descriptions.
public class ParsedCommand
This is a simple, immutable class with three fields:
Command command: a command to be executed.String commandStr: the string that the command was parsed from; maintained for the purpose of logging and formulating error messages.String argString: the argument associated with this command, if any.
Mainly for testing support, we will override toString() and equals(Object); and, as required when overriding equals, we will override hashCode().
class CommandReader
This class is responsible for obtaining and parsing lines in the form command argument, for example:
set a=5
public CommandReader( BufferedReader reader )The reader identifies the input source. Note that this might be the console or a file, among other things.
public ParsedCommand nextCommand( String prompt ): If prompt is non-null, it is printed to stdout. The method then reads a line of input, parses it into command and argument parts, and returns a ParsedCommand object encapsulating the result. Leading and trailing spaces are discarded. Blank lines and comments are ignored. The command part of the string is converted to a Command enum constant based on:
-
- Its spelling (case-insensitive)
- Interpretation of shortcuts; for example, x= is interpreted as a shortcut for XEQUALS.
public Stream<ParsedCommand> stream(): returns a Stream<ParsedCommand> consisting of input lines parsed and converted to ParsedCommands per the rules described in method nextCommand.
public static ParsedCommand parseCommand( String line ): parses the input and instantiates a ParsedCommand in accordance with the parsing specification. The input is assumed to be non-null, non-empty, and trimmed.
class Result
An object of this class is used to describe the status of an operation. It is immutable. It contains a boolean variable indicating the operation status and a list of messages (usually error messages, if any) associated with that status. It has two constructors and two public methods.
public Result( boolean success )Creates a Result object with the given status and an empty list of messages.
public Result( boolean success, List<String> message )Creates a Result object with the given status and a copy of the given list of messages.
public boolean isSuccess()Returns the status of this Result.
public List<String> getMessages()Returns the list of messages associated with this Result.
class ValidationException extends RuntimeException
This unchecked exception is associated with validation failures during expression evaluation (which, in theory, should never occur because all expressions should be validated before evaluation). It has constructors corresponding to all the public constructors in the superclass.
class InputParser
This class contains an Equation, which encapsulates the expressions associated with a simple function or a parametric equation (see below for details). Its responsibility is to take a command and its associated argument, and translate them into operations on the contained equation. It has two constructors and two public methods. Note that, as discussed below, Equation is an interface, and Exp4jEquation is a concrete class that implements Equation.
public InputParser(): constructor. Initializes the state of this object with an Exp4jEquation.
public InputParser( Equation equation ): constructor. Initializes the state of this object with the given Equation. The input argument may be null, in which case a new Exp4jEquation is instantiated.
public Equation getEquation(): returns the contained equation.
public Result parseInput( Command command, String argString ): translates the given command and argument into operations on the contained equation. It returns a Result object that describes the operation’s status. It processes commands as follows:
-
- EQUATION:
creates a newly initialized equation. - XEQUALS:
sets the expression responsible for generating x-values to the corresponding argument. If the argument is empty, it prints the current expression to stdout. - YEQUALS:
sets the expression responsible for generating y-values to the corresponding argument. If the argument is empty, it prints the current expression to stdout. - SET:
declares the variables contained in the corresponding argument. A variable may optionally be assigned a value using the equal sign followed by any valid expression. Variables not assigned explicit values will have an initial value of 0. If the argument is empty, it prints the current values of all variables to stdout. An example of using this command:set a=5,n=2,theta=pi/6,variant=cos(pi/2),x,y
- START:
sets the start value of the iteration range to the value of the corresponding argument, which may be an expression. If the argument is empty, it prints the current value to stdout. - END:
sets the end value of the iteration range to the value of the corresponding argument, which may be an expression. If the argument is empty, it prints the current value to stdout. - STEP:
sets the increment value for traversing the iteration range to the value of the corresponding argument, which may be an expression. If the argument is empty, it prints the current value to stdout. - INVALID:
returns an unsuccessful result. - PARAM: describes the name of the parameter in a parametric equation. An example of using this command:
param t
- EXIT, NONE, YPLOT, XYPLOT, OPEN, SAVE: these commands are ignored; interpreting and executing them are left to the implementing application. A successful result is returned.
- EQUATION:
public interface Equation
This interface describes the facilities necessary to manage an equation. An equation is a set of resources that defines a simple function (y = f(x)) or a parametric equation ((x,y) = f(t)). These resources include:
- Expressions for the calculation of y values in a simple function, or the calculation of (x,y) values in a parametric equation;
- The declaration of variables used in the expression(s), and
- A range for producing a plot.
At this time, the only implementing class is Exp4jEquation, which is based on the exp4j API. The following operations are described:
Equation newEquation(): Returns a newly initialized Equation.
[Design note: this is declared as an instance method because that’s the way it’s declared in the Equation interface. Future revisions of the interface may remove this method.]void setVar(String name, double val): Sets the value of a variable to a given value.void removeVar(String name): Removes the variable with the given name.Double getVar(String name): Gets the value of the variable with the given name.Map<String,Double> getVars(): Returns an unmodifiable map describing all declared variables and their values.Result setYExpression(String exprStr): Parses the expression used to derive the y-coordinate of a point from the given string.Result setXExpression(String exprStr): Parses the expression used to derive the x-coordinate of a point from the given string.String getXExpression(): Gets the currently set x-expression.String getYExpression(): Gets the currently set y-expression.Stream<Point2D> yPlot(): Iterates over the encapsulated range, generating the (x,y) coordinates derived from an equation of the form y=f(x).Stream<Point2D> xyPlot(): Iterates over the encapsulated range, generating the (x,y) coordinates derived from a parametric equation.void setParam(String param): Sets the name of the parameter used in a parametric equation (the default is t).String getParam(): Gets the name of the parameter used in a parametric equation.void setRange(double start, double end, double step): Sets the iteration parameters for this Equation.void setRangeStart(double rangeStart): Sets the start of the iteration range for this Equation.void setRangeEnd(double rangeEnd): Sets the end of the iteration range for this Equation.void setRangeStep(double rangeStep): Sets the increment value for traversing the iteration range for this Equation.boolean isValidName( String name ): Determines whether the given string is a valid variable name.boolean isValidValue( String value ): Determines whether the given string is a valid value.Optional<Double> evaluate( String exprStr ): Parses and evaluates an expression in the context of the current equation. Declared variables and functions are recognized; use of undeclared variables or functions will result in evaluation failure. The result is returned via an Optional object; if the operation fails, an empty Optional will be returned.
public class Exp4jEquation implements Equation
This class implements the Equation interface using the exp4j API.
Summary
In this lesson, we examined the exp4j API for parsing mathematical expressions. We discussed the need to gather textual input and parse it using exp4j to configure equations for plotting in the Cartesian plane. Finally, we designed components to gather and execute the textual configuration data. In the next lesson, we’ll look at an implementation of these and related facilities.
Next:
Cartesian Plane Lesson 10: User Input: Implementation (Page 1)