Java-Bibliothek zum Umwandeln einer mathematischen Formel in einen AST

Ich suche nach einer Java-Bibliothek, die mathematische Formeln in einen AST (abstrakten Syntaxbaum) zerlegen kann.

UPDATE:
Ich bin offen für Alternativen in anderen Sprachen als Java, vorausgesetzt, ich kann diese Tools/Bibliotheken von Java aus aufrufen.
Beispielsweise kann JavaScript mithilfe der Rhino-Engine eingebettet werden .

Essentielle Anforderungen:

  1. Die Fähigkeit , Formeln in Infix - Notation zu analysieren .

  2. Die Fähigkeit, unbekannte Variablen zu erhalten – ich suche keinen Taschenrechner.

  3. Eine anpassbare Liste von Operatoren und Funktionen.
    Es wäre auch hervorragend, wenn man bereits eingebaute Funktionen (zB sin(x)) entfernen könnte.

Nicht wesentliche Anforderungen:

  • Die Bibliothek kann Open Source sein, muss es aber nicht. Eine kostenlose Bibliothek reicht aus.
Hinweis: Auch wenn ich inzwischen eine mehr oder weniger passende JavaScript-Bibliothek gefunden habe, sind bessere Alternativen (vorzugsweise in Java) immer noch willkommen!

Antworten (6)

Math.js

Der JavaScript-Teil

„Math.js ist eine umfangreiche Mathematikbibliothek für JavaScript und Node.js.“
Projekt-Readme

Es bietet eine parse()- Funktion.
Beispiel mit der NodeJS-Umgebung:

var math = require('mathjs')();
var ast = math.parse('xy^(1/2)');

// Fully log the object
var util = require('util');
console.log(util.inspect(ast, {showHidden: false, depth: null}));

Ausgabe:

{ op: '^',
  fn: 'pow',
  params:
   [ { name: 'xy' },
     { op: '/',
       fn: 'divide',
       params:
        [ { valueType: 'number', value: '1' },
          { valueType: 'number', value: '2' } ] } ] }

Der Java-Teil

Ich verwende die Java Nashorn VM (nur verfügbar in Java >= 8), um JavaScript auszuführen.

Programmarchitektur:

User ---------------> Java
      inputs formla    |----> Nashorn ----> math.js
                       |<---------------------|
User <-----------------|

Die Verwendung der Nashorn-Engine ist recht einfach (Ausnahmebehandlung weggelassen)

ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval(readerInstancePointingToMathJsLibrary);
engine.eval(readerInstancePointingToBridgeJavaScript);

Der JavaScript-Code der Bridge hängt stark von Ihrer Implementierung von AST-Knoten ab. Wir nutzen die Fähigkeit von Nashorn, Java-Objekte in JavaScript zu erstellen und nach Java zu übertragen. Beispiel:

var math = mathjs();
function convert(formula) {
    var ast = math.parse(formula);

    var javaAst = /* build your AST with Java objects */
    return javaAst;
}

Wir können jetzt von Java aus auf diese Funktion zugreifen und sogar beliebige Argumente übergeben:

Invocable inv = (Invocable) engine;
// Expression is my AST node type in Java
expr = (Expression) inv.invokeFunction("convert", formulaFromUser);

Hinweis: Ich brauchte eine schnelle Möglichkeit, mathematische Ausdrücke zu analysieren. Ein Parser (entweder handschriftlich oder von einem Parser-Generator generiert) ist immer vorzuziehen. Nichtsdestotrotz zeigt der obige Code, wie Java Nashorn einfach integriert werden kann.

JavaScript

Vor einigen Monaten habe ich Esprima verwendet , um solche Eingaben zu analysieren. Eigentlich parst Esprima jede JavaScript-Eingabe (wandelt sie in einen Baum um), also sollte es für solche mathematischen Ausdrücke funktionieren.

Nachdem Sie Esprima hinzugefügt haben, können Sie Folgendes tun:

esprima.parse(input);

... wo inputist ein String, der die Eingabe enthält, die analysiert werden soll (wenn sie ungültig ist, wird ein Fehler ausgegeben).

Beispiel

esprima.parse("1+2*3")

gibt das folgende Objekt zurück:

{
    "type": "Program",
    "body": [
        {
            "type": "ExpressionStatement",
            "expression": {
                "type": "BinaryExpression",
                "operator": "+",
                "left": {
                    "type": "Literal",
                    "value": 1,
                    "raw": "1"
                },
                "right": {
                    "type": "BinaryExpression",
                    "operator": "*",
                    "left": {
                        "type": "Literal",
                        "value": 2,
                        "raw": "2"
                    },
                    "right": {
                        "type": "Literal",
                        "value": 3,
                        "raw": "3"
                    }
                }
            }
        }
    ]
}

Ich habe den Esprima-Code modifiziert und ihn in einem experimentellen Projekt verwendet, um benutzerdefinierte Operatoren in JavaScript zu definieren. Die Anwendung ist Open Source auf GitHub: http://ionicabizau.net/JavaScript-custom-operators/

Esprima scheint weitaus leistungsfähiger und in der Lage zu sein, Funktionen zu analysieren, als ich eigentlich brauche. Trotzdem sind +1 und Ihre Operatoren sehr interessant.

Java

Es scheint, dass JEP ein mathematischer Ausdrucksparser ist.

JEP ist eine Java-API zum Analysieren und Auswerten mathematischer Ausdrücke. Mit dieser Bibliothek können Sie Ihren Benutzern erlauben, eine beliebige Formel als Zeichenfolge einzugeben und sofort auszuwerten. JEP unterstützt benutzerdefinierte Variablen, Konstanten und Funktionen. Eine Reihe allgemeiner mathematischer Funktionen und Konstanten sind enthalten.

Merkmale

  • Benutzerfreundliches Paket zum Analysieren mathematischer Ausdrücke
  • Kleine Größe (nur 56kb als JAR-Archiv)
  • Unterstützt boolesche Ausdrücke (!, &&, ||, <, >, !=, ==, >= und <=)
  • Schnelle Auswertung (der Ausdruck kann schnell für verschiedene Variablenwerte ausgewertet werden)
  • Enthält allgemeine mathematische Funktionen
  • Erweiterbar durch benutzerdefinierte Funktionen
  • Vordefinierte Konstanten wie 'pi' und 'e'
  • Unterstützung für Zeichenfolgen, komplexe Zahlen und Vektoren
  • Unterstützung für implizite Multiplikation (ermöglicht die Verwendung von Ausdrücken wie „3x“ anstelle von „3*x“)
  • Ermöglicht die Wahl zwischen deklarierten und nicht deklarierten Variablen
  • Kompatibel mit Java 1.1 (getestet mit Sun Java JDK 1.1.8 und der Microsoft Java VM)
  • Unterstützt Unicode-Zeichen (einschließlich griechischer Symbole)
  • Enthält JavaCC-Grammatik, aus der die Hauptklassen generiert werden

Es ist Open Source auf SourceForge .

Außerdem gibt es eine SO-Frage zu diesem Thema: https://stackoverflow.com/q/4589951/1420197

Das ursprüngliche Projekt scheint eingestellt zu werden. Die SourceForge-Projektseite verlinkt auf eine neue externe Seite , die Jep Java als kommerzielles Produkt anbietet (550 Dollar für Binärdateien, 950 Dollar für den Quellcode).
@ComFreek Du hast Recht. Wie auch immer, ich denke, das anfängliche Projekt sollte abdecken, was Sie brauchen. Vielen Dank für das Kopfgeld! :-)

Das Erstellen von Formelparsern/Baumerstellern ist eine ziemlich einfache Übung. Sie können nach einer Bibliothek suchen, aber am Ende werden Sie sie immer modifizieren, um genau das zu produzieren, was Sie wollen. Stattdessen ist es wahrscheinlich einfacher, einfach zu codieren, was Sie wollen.

  • Dieser StackOverflow-Link enthält Anweisungen zum einfachen Erstellen von Parsern wie diesem von Hand. Es bietet auch Zugriff auf einen zweiten Link, der zeigt, wie man solche Parser einfach in solche umwandelt, die ASTs erzeugen.
  • Sie können ganz einfach anpassen, dass Sie beliebige Infix-Operatoren, beliebige Funktionsoperatos ("sin"), skalare Werte und Variablennamen einfügen möchten.
  • Sie können diese Art von Parsern in praktisch jeder Sprache codieren, einschließlich Java.

Wenn Sie etwas erheblich Komplexeres als einen Ausdruck parsen möchten, können Sie diese Art von Parser dazu drängen, aber in diesem Fall ist es im Allgemeinen einfacher, zum Parser-Generator zu wechseln.

Java

Symja wandelt einen mathematischen Ausdruck in die folgende Symja AST um .

Ich habe javassist verwendet, um Java-Ausdrücke in Bytecode zu konvertieren (was Sie letztendlich suchen, wenn Sie den Ausdruck auswerten möchten). Es kann sogar bestehende Funktionen neu definieren.

http://www.csg.ci.iu-tokyo.ac.jp/~chiba/javassist/

Verzeihen Sie mir, wenn ich Sie missverstanden habe, aber wie lässt mich Javassist Benutzereingaben (Strings) in eine Baumstruktur (Java-Objekte) parsen?
Wahrscheinlich nicht. Sie können Ihren Ausdruck in eine Java-Methode kompilieren, die Sie dann direkt aufrufen können, um sie auszuwerten. Das liegt daran, dass ich erwartet hatte, dass dies Ihr endgültiges Ziel war. Wenn dies Teil einer Übung zum Schreiben eines Compilers ist, sollten Sie es stattdessen von Hand tun :)