Userinput String to formula

Hey I want to write a program where the user can enter a formula in a text field and that program then converts this string to the “correct” formula and then calculates it.

Here a example:

//This would appear in front of the text input: x = 
String userInput = "x*2";
float x;
void calculateX(String userInput){
    x = userInput;
   //the computer should then calculate x = x * 2
}

The problem is I dont know how I can turn a string in programm code…

I hope you know what I mean.
Thank you in advance :slight_smile:

Hi,

What you are trying to do is roughly what the parser is doing with your code : you give him a input string (your source code) and the parser split it into tokens that are then used to create an abstract syntax tree of your program.

You can refer to the Wikipedia article on the parsing process :

The steps are (simplified) :

  • Lexical analysis (split the input string into tokens defined by regular expressions like name identifiers, key words, numbers, commas, line breaks…)

  • Syntactic analysis → build the Abstract Syntax Tree of the program : it’s representing the program structure as a tree where the leaves are variables or values and the non-leaf nodes are operations or operators

  • The last step is the execution. In real programming languages, some machine code is usually generated by using the AST but in your case you might want to just evaluate the tree and return the result.

So this is not trivial, you need to make a program that takes a string as input, split the string into tokens (variables like x or y, function names like cos, exp, numbers 2, 6.4 or 4e10 and operators +, *…) then construct the syntax tree with those tokens (taking into account operator precedence, associativity, parenthesis, function calls…)

For example the 4 + 3 * 5 / (8 - x) expression gives the following AST :

Note : the above expression is equivalent to 4 + ((3 * 5) / (8 - x)) with parenthesis (which enforce precedence)

In order to execute this expression, you need to construct the AST in your code. This is where object oriented programming comes handy because you every token is basically a node (whether it’s a variable, operator or number) and you can evaluate it :

For example any number evaluate (the result is) to itself : 44
A binary operator evaluate to the operator applied on the left and right member : +(4, 5)4 + 59
A variable is just a lookup in a lexical environment table with values associated to variable names: { x: 4 }

So you have this abstract base class for each node :

abstract class ASTNode {
  float eval();
}

And the concrete implementations of the binary operator and value :

class ValueNode extends ASTNode {
  float value; // The actual value

  ValueNode(float value) {
    this.value = value;
  }
  
  // Must implement abstract method
  float eval() {
    return value; // Simply return the value
  }
}

// Usage : 

ASTNode number = new ValueNode(5);
println(number.eval()); // -> 5
class BinOp extends ASTNode {
  String op; // The operator '+', '*', '/'...
  ASTNode left, right; // Note that we use the abstract class

  BinOp(String op, ASTNode left, ASTNode right) {
    this.op = op;
    this.left = left;
    this.right = right;
  }

  float eval() {
    // Getting the left and right values of the subtree
    float a = left.eval(): // Note that it's recursive
    float b = right.eval();

    // Switch on the operator...
    switch(op) {
      case "+":
        return a + b;
      case "-":
        return a - b;
      default:
        return -1; // error
    }
  }
}

// Usage
ASTNode add = new BinOp("+", new ValueNode(4), new ValueNode(2));
println(add.eval()); // -> 6

So this is roughly how it works but in a more advanced compiler architecture, you would use some design patterns like the Visitor.

You can take a look at this tutorial for more information :

Also if you want to do this in Java there’s useful links on this thread :yum: :

3 Likes

Great write up @josephh !

@Flolo what is this for?

2 Likes

The code in functionF I want to change with the user input…
I would recomment to run it in tweak mode…

Here is the code:

float minX = -10;
float maxX = 10;
float minY = -10;
float maxY = 10;

float cellSize;

float translateX;
float translateY;

void setup() {
  size(900, 900);

  translateX = 400;
  translateY = 400;

  cellSize = float(width) / (maxX);
  print(cellSize);
}
void draw() {
  translateX = 400;
  translateY = 400;

  translate(translateX, translateY);
  background(0);

  stroke(255);
  strokeWeight(20);
  point(0, 0);

  strokeWeight(5);
  line(-width/2-translateX, 0, width/2+translateX, 0);
  line(0, -height/2-translateY, 0, height/2+translateY);


  stroke(255);
  strokeWeight(2);
  noFill();
  beginShape();
  for (float x = minX; x < maxX-1; x += 0.01) {
    float y = functionF(x);

    vertex(x*cellSize, y*cellSize);
  }
  endShape();
}
float functionF(float x) {
  float y = 0.25*(x*x);




  return -y;
}

If you are using Java mode then I suggest the Jasmine library which was designed to create expressions from strings and then evaluate them very quickly.

Jasmine can be installed in Processing from the Contributions manager and detailed information about using the library can be found here.

3 Likes

A quick demo of Jasmine to calculate the area of circles with radii from 1 to 5 inclusive

import org.quark.jasmine.*;

String formula = "PI*r^2";

Expression expr = Compile.expression(formula, false);
for(int i = 1; i <= 5; i++){
  float area = expr.eval(i).answer().toFloat();
  println("Radius = "  + i + "   Area of circle = " + area);
}

Output is

Radius = 1   Area of circle = 3.1415927
Radius = 2   Area of circle = 12.566371
Radius = 3   Area of circle = 28.274334
Radius = 4   Area of circle = 50.265484
Radius = 5   Area of circle = 78.53982
4 Likes

a versatile option (but can go wrong) is to use builtin Nashorn JavaScript interpreter:

import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptContext;
import javax.script.ScriptException;
import javax.script.Invocable;

private static ScriptEngineManager engineManager;
private static ScriptEngine nashorn;

void setup() {
  size(400, 400, P3D);

  String[] options = new String[] { "--language=es6" };
  jdk.nashorn.api.scripting.NashornScriptEngineFactory  factory = new jdk.nashorn.api.scripting.NashornScriptEngineFactory();
  nashorn = (jdk.nashorn.api.scripting.NashornScriptEngine) factory.getScriptEngine(options);

  try {
    Object res = nashorn.eval("5 * 2.2");
    println(res);

    res = nashorn.eval("Math.sin(Math.PI / 4)");
    println(res);

    res = nashorn.eval("'hello'.length");
    println(res);
  }
  catch (Exception e) {
    e.printStackTrace();
  }
}

outputs

11.0
0.7071067811865475
5
2 Likes

Hey thanks for the help and the examples :slight_smile:
I appreciate it.