Heiner Kücker

Expression Parser

Home

Java-Seite

   Bit Packed Array

   ASM Improved

   heterogene
   Map, HMap

   Constraint
   Code Generator

   JSP WorkFlow
   PageFlow FlowControl
   Page Flow Engine
   Web Flow Engine
   Control_and_Command

   JSP_Spreadsheet

   Code-Generator
   für Option-Either-Stil
   in Java

   verbesserter
   Comparator

   Fluent-Interface
   Code-Generator
   auf Basis
   einer Grammatik

   Visitor mit Multidispatch

   for-Schleife mit
   yield-return

   Kognitions-Maschine
   semantisches Netz

   Domain Parser

   Codegenerator_für
   hierarchische
   Datenstrukturen

   Expression_Engine
   Formula_Parser

   Thread Preprocessor

   State Transition Engine

   AspectJ

   Java_Explorer

   DBF_Library

   Kalender_Applet

   SetGetGen

   BeanSetGet

   CheckPackage

   LineNumbers

   GradDms

   Excel-Export

   StringTokenizer

   JspDoc

   JspCheck

   JSP-Schulung
   Java Server Pages
   Struts

   Ascii-Tabellen-
   Layouter

   Ascii-Baum-
   Layouter

   Ascii-Art-Fluss-
   Diagramm-
   Parser

   AsciiArt
   AssignmentMatrix
   Layouter

   StringSerial

   Silbentrennung

   JDBC_Schlüssel-
   Generierung

   bidirektional/
   unidirektional
   gelinkte Liste

   Java_Sitemap
   Generator

   XmlBuilder

   RangeMap

   StringFormatter

   VersionSafe
   XCopy

   JTextField

   CommandLine-
   ParamReader

   Bitmap-Grafik

   MultiMarkable-
   Buffered-
   InputStream

   JavaCache

   JdomUtil

   CollectionUtil

   XML Really
   Pull Parser

   Log-Filter

   Remote-Protokoll

   Sudoku-Generator

   Delegation statt
   Mehrfachvererbung

   Disjunct
   Interval Set

   WebCam_Demo

   Weiterentwicklung_Java

Alaska-XBase++-Seite

Projekte

Philosophien
Techniken


Konzepte

Sudoku

Kontakt /
Impressum


Links

SiteMap





Letzte Aktualisierung:
25.08.2005

Expression Parser

Ein Ausdrucksparser oder Formelparser, der Ausdrücke wie 3 + 3 unter Einhaltung der Operatorpriorität zur Laufzeit abarbeiten kann.
Er beherrscht Variable, Arrays, Listen, Maps und Built-In-Funktionen.
Ich habe ihn für die Integration in das CnC-Framework geschrieben.

Online-Tester für den Expression-Parser auf www.control-and-command.de .
Bitte im Hauptmenü den Link zum Expression-Parser wählen (relativ weit unten).
Aus Zeitgründen habe ich für den Tester kein eigenes Login programmiert.

Anleitung Expression

Die Klasse Expression mit ihren dazugehörenden Packages kann einen als String übergebenen Ausdruck Parsen, Compilieren und Ausführen.
Sie ist als Baustein meines Fluss-Steuerungs-Frameworks CnC (Control-and-Comand) gedacht.
Mit der Einführung der Sequenzen ist es nun möglich mehrzeilige Ausdrücke zu schreiben. Der Zeilenumbruch ist nun ein normales weisses Zeichen.

Parsen und Kompilieren:

    try {
      Expression exp = Expression.parse("a+b");
    } catch (ExpressionParseException exc) {
      System.err.println(exc.getMessage());
    }

Typ-Prüfung beim Parsen/Kompileren

Der Expression-Parser führt eine vorausschauende Typ-Prüfung durch. Dadurch werden offensichtliche Fehler, wie numerische Operationen auf Strings, abgelehnt. Variablen-Typisierung
Wenn eine Variable mit

 var <varName> <varClass>

typisiert wurde, kann der Typ auch beim Zugriff auf Variablen geprüft werden.
Anderenfalls kann der Typ nicht zur Übersetzungszeit festgestellt werden, so dass hier keine Typ-Prüfung erfolgt.

Ausführen

Ein einmal kompilierter (geparster) Ausdruck kann beliebig oft ausgeführt (evaluiert) werden.

    Object oValue = null;
    try {

      //Laufzeit-Umgebung mit Variablen
      AbstractRuntimeEnvironment runEnv = new StandaloneRuntimeEnvironment() ;
      //Setzen Anfangsvariable
      runEnv.setVariable("a", new Double(1));
      runEnv.setVariable("b", new Double(2));

      oValue = exp.eval( runEnv ); // siehe oben: "a+b"
    } catch (ExpressionEvaluationException exc) {
      System.err.println(exc.getMessage());
    }
    System.out.println( "oValue:"+oValue);

RuntimeEnvironment

Das RuntimeEnvironment enthält die Variablen mit ihrem Namen als Key und ihrem Value als Objekt. Denkbar sind noch Erweiterungen wie Locale und Betriebsartenschalter.

Im CnC-Framework (JSP-Fluss-Steuerung Workflow) wird hier die Umgebung des CnC, also der JSP-Adressraum, request, session und application eingesetzt.

Variablen-Namen sind case-sensitive, dass heisst, Gross-/Kleinschreibung wird unterschieden.

Im StandaloneRuntimeEnvironment wird eine HashMap für die Variablen verwendet. Später sind folgende Erweiterungen möglich:

- lokale Vars (Stack),

- Methode posteval für Funktionalität preSetLater oder postSetLater (siehe Inline-Operatoren)

Datentypen

Alle Java-Elementar-Typen werden durch Objekte ihrer Wrapper-Klassen gespeichert.
Die Typ-Definition ist in der Klasse de.cnc.expression.Types hinterlegt.
Es werden folgende Typen unterstützt:

-Numerisch:

BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, Short (java.lang.Number)

aus Genauigkeitsgründen wird intern immer mit BigDecimal gerechnet

-Boolean

Für Boolsche Werte gibt es die reservierten Worte true und false.
Intern werden stets die Boolean-Wrapper-Klassen verwendet.

-String (eventuell später noch StringBuffer unified)

Strings sind in folgender Schreibweise möglich:

"abc"

'abc'

Die Möglichkeit der Verwendung von Hochkommas in Verbindung mit den reservierten Worten apostroph oder quote sowie highcomma oder higComma erlaubt die Lösung von Problemen mit Esc-Sequenzierung/Encoding

Beispiel input-Tag:

input value="<%= Expression.eval("'abc'+apostroph+'def'")%> ...

Dadurch ist auch die Verwendung meiner kleinen Expression-Library in JSP-Tags möglich.

Zur Realisierung eines dynamischen Verhaltens kommt noch ein Typ Expression hinzu.
Als Erweiterung wäre es sinnvoll statt eines eigenen Typsystems ( siehe de.cnc.expression.Types ) im Expression-Parser die Java-Klassen als Typen zu verwenden. Das realisiere ich eventuell später.

-Datum/Uhrzeit

Datum und Uhrzeit habe ich in der Expression-Engine besondere Aufmerksamkeit geschenkt. Es gibt reservierte Worte für Wochentage und Monate. Datums-Angaben (nicht fertig) Uhrzeit-Angaben und kombinierte Datums-Uhrzeit-Angaben (nicht fertig).

Eine Uhrzeit-Angabe (Uhrzeit-Literal) hat die Notation
HH:mm (Beispiel: 0:00 bis 23:59)
HH:mm:ss (Beispiel: 0:00:00 bis 23:59:59)
HH:mm:ss.S (Beispiel: 0:00:00.00 bis 23:59:59.9999999)
und liefert ein java.sql.Time-Objekt zurück.
Die Angabe der Stunden erfolgt in 24-Stunden-Angabe.

In der Java-Klasse CompareUtil sind Vorkehrungen (Sonderlocke) für den Vergleich von Uhrzeiten (java.sql.Time), Wochentagen (de.cnc.expression.util.DayOfWeek) und Monaten mit Datum-Uhrzeit-Kombinationen (java.util.Date) eingebaut. Beim Vergleich einer Uhrzeit, eines Wochentages oder Monates mit einer Datum-Uhrzeit-Kombination wird aus der Datum-Uhrzeit-Kombination der jeweilige interessierende Anteil (Uhrzeit, Wochentag oder Monat) ermittelt und verglichen.

Dadurch ist es möglich, Ausdrücke für Zeiträume und Termine wie sie in Kalendern, Plantafeln (PPS Timeline), Schedulern usw. benötigt werden, zu formulieren.

Hier ein Beispiel für einen Ausdruck zur Berechnung, ob ein Wochenendzuschlag gewährt wird. (nicht fertig testen)

date == SUNDAY || date == SATURDAY
Hier ein Beispiel für einen Ausdruck zur Berechnung, ob ein Nachtschichtzuschlag gewährt wird. (nicht fertig testen)
date <= 6:00 && date >= 22:00
Hier ein Beispiel für einen Ausdruck zur Berechnung, ob der Termin in den Sommer-Monaten liegt. (nicht fertig testen)
date >= MAY && date <= SEPTEMBER
Siehe hierzu auch mein Konzept zur effektiven Auswertung von ausdrucksbasierten temporalen Datenstrukturen TimelineStructure.

-Collections, Arrays, Listen und Maps:

Über den Index-Operator var[ i ] ist das Ansprechen von Elementen in Arrays, Implementationen von java.util.List (ArrayList usw.) sowie Implementationen von java.util.Map (HashMap usw.) möglich.

Dabei können die drei Collection-Arten (Array, List, Map) beliebig ineinder geschachtelt werden.

arrListMap[ 0 , 0 , 'A' ]
oder
arrListMap[ 0 ][ 0 ][ 'A' ]
oder
arrListMap[ 0 ][ 0 , 'A' ] (auch beliebig Komma oder neues Bracket-Area gemischt)

Neben Variablen-Namen können noch Sequenzen {...}, ParenthesisAreas (...) und Funktionsaufrufe mit einem Index-Operator ausgestattet wrden.

newStringArray( 'str0' , 'str1' )[ 0 ]
( newStringArray( 'str0' , 'str1' ) )[ 0 ]
{ newStringArray( 'str0' , 'str1' ) }[ 0 ]

Die Zuweisung zu indizierten Elementen von Variablen ist auch möglich:
arrListMap[ 0 , 0 , 'A' ] := 'Test1'

Die Zuweisung zu Collection-Elementen die von Ausdrücken zurückgegeben werden, die keine Variablen sind, ist noch nicht möglich.
( arrListMap )[ 0 , 0 , 'A' ] := 'Test1'

-Sequenzen:

Für Sequenzen ist ein eigener Abschnitt reserviert.

Denkbare Erweiterungen:
Referenzvariable (nicht fertig)
Instanziieren von Objekten beliebiger Klassen über Class.forName() newInstance (nicht fertig) und Methodenaufruf darauf

Sequenzen

Sequenzen ermöglichen das Einbringen algorithmischen Verhaltens in die Expression Library.
Die Anregung zum Einbau der Sequenz-Funktionalität stammt von Stefan Mathias Aust im Diskussionforum de.comp.lang.java

Für Sequenzen gilt folgende Schreibweise:

{ <expression> [ ; <expression> ] }

Eine Sequenz wird in geschwungene Klammern eingeschlossen und enthält Null bis beliebig viele Expressions, die durch Semikolon getrennt sind.
Leere Expressions, dass heisst Semikolon folgt auf Semikolon beziehungsweise Semikolon steht am Anfang oder Ende, sind erlaubt.

Die einzelnen Expressions einer Sequenz werden nacheinander ausgeführt.

Zurückgegeben wird der Rückgabewert der letzten Expression in der Sequenz.
Alternativ kann die Sequenz auch mit der returnSequ/breakSequ-Funktion abgebrochen werden.
Dabei wird der Wert des Ausdruckes, welcher der return-Funktion übergeben wurde zurückgeliefert.

Anwendung der Sequenzen:

Sequenzen kann man als Parameter für die if/iif-Funktion und die while/iwhile- Funktion verwenden:

if( bedingung , trueExpression/Sequenz , falseExpression/Sequenz )

while( bedingung , bodyExpression/Sequenz )

repeat( bedingung , bodyExpression/Sequenz )

for( startExpression , repeatCondition , stepExpression , bodyExpression )
for( i := 0 , i <= 3 , i++ , { print( 'for:' ) ; println( i ) } )

Das else-if-Konstrukt könnte man folgendermassen aufbauen:

if( bedingung1,
 thenExpression/Sequenz ,
 if( bedingung2 ,
  elseifExpression/Sequenz ,
  elseExpression/Sequenz ) )

breakSequence( <objectvalue> )
breakSequ ( <objectvalue> )
Bricht die aktuelle Sequenz ab und liefert den als Parameter übergebenen Wert zurück.

continueSequence()
contSequ()
Startet die aktuelle Sequenz von vorn.

Denkbare Erweiterungen

Parameter an Sequenzen übergeben: {|parameterListe|body} (nicht fertig) (falls noch jemand den Clipper/Alaska kennt, weiss er, woher diese Notation stammt)

break continue für die while-Funktion

Mit Hilfe der Funktionen compile und eval können Sequenzen Variablen zugewiesen und später ausgeführt werden.

Wünschenswert sind weiterhin noch lokale Variable (nicht fertig),
Referenzen auf Sequenzen (nicht fertig),
Parameter für Sequenzen (nicht fertig) und
zusätzliche Parameter für die eval-Methode (nicht fertig).

Definition:
funcRecursiv := { | x | if( x > 0 , { eval( funcRecursiv , x - 1 ) } , null ) }

Aufruf:
eval( funcRecursiv , 5 )
Konstrukte wie Function-Pointer (Funktionen höherer Ordnung) und Closures wären damit ohne weitere Massnahmen möglich.

Ausrechnen von konstanten Ausdrücken, Sub-Ausdrücken, zwischenspeichern der konstanten Werte, Löschen der konstanten Werte in tieferen Aufrufhierarchien zur Speicherplatzeinsparung

Kommentare

Die Einführung der Sequenzen und der mehrzeiligen Notation erfordert die Möglichkeit der Kommentare.
// Inline-Kommentare
/* ... */ Bracket-Area-Kommentare

Reservierte Worte

true
false
null
apostroph quote " (siehe hierzu Strings, Problem Encoding)
highcomma highComma ' (siehe hierzu Strings, Problem Encoding)
newline newLine \n (siehe hierzu Strings, Problem Encoding)

and
or
nand
nor
xor

pi PI
euler Euler EULER Eulersche Zahl, Basis des natürlichen Logarithmus

SUNDAY Sonntag (0)
MONDAY Montag (1)
TUESDAY Dienstag (2)
WEDNESDAY Mittwoch (3)
THURSDAY Donnerstag (4)
FRIDAY Freitag (5)
SATURDAY Samstag (6)

JANUARY Januar ( 0)
FEBRUARY Februar ( 1)
MARCH März ( 2)
APRIL April ( 3)
MAY Mai ( 4)
JUNE Juni ( 5)
JULY Juli ( 6)
AUGUST August ( 7)
SEPTEMBER September ( 8)
OCTOBER Oktober ( 9)
NOVEMBER November (10)
DECEMBER Dezember (11)

Konstante

StringLiteral
 eingeschlossen in Apostrophe "..." oder Hochkomma '...',
 javaübliche Esc-Sequenzen sind erlaubt: \\ \" \b \n \r \t

NumberLiteral
 negatives Vorzeichen erlaubt, Dezimalpunkt am Anfang oder zwischen zwei Ziffern, (noch) keine Exponentialdarstellung

Variable

Name beginnt mit Buchstabe, danach Buchstaben oder Zahlen, Länge maximal 30 Zeichen, Punkte (später) erlaubt (zum Beispiel: session.user)

Indexoperator für Arrays und Implementationen von java.util.List oder java.util.Map

Array-, Listen und Map-Elemente (java.util.List) werden über einen Index angesprochen (Indexoperator)

varArr[ index ]

Solche Kombinationen sind natürlich auch möglich:

varArr[ index++ ]
varArr[ ++index ]

DotOperator zum Ansprechen von Objekten

Objekt-Attribute (Member) und Objekt-Methoden werden über den Punkt-Operator angesprochen (Dot-Operator)

Über object.member können public-Member oder die entsprechenden get- und set-Methoden lesend und schreibend angesprochen werden.

Über object.methode( Parameterliste ) können public-Methoden aufgerufen werden.

Bei Parameterwerten null oder bei Parameter-Objekten von erweiterten Klassen kann es passieren, dass nicht die korrekte Methode aus eventuell mehreren überladenen Methoden mit der gewünschten Signatur aufgerufen wird.
Dafür baue ich noch einen Cast-Operator ein (nicht fertig).
Alternativ können für numerische Werte (Byte, Short, Integer, Long, Float, Double) sowie Character anstatt String die entsprechenden Konvertierungsfunktionen (nicht fertig) benutzt werden.
Ein weiteres Problem tritt bei Elementartypen und Objekten in Methoden-Signaturen auf.
Zum Beispiel gibt es bei der java.util.ArrayList
remove( Object )
und
remove( int )
Beim Aufruf list.remove( 1 ) wird die jeweils zufällig zuerst gefundene Methode aufgerufen. Hier kann man besser mit der removeAt-Funktion arbeiten.
Für andere Konstellationen gibt es aber (noch) keine Lösung.

Der Zugriff auf Objekt-Attribute und der Aufruf von Methoden kann im AbstractRuntimeEnvironment abgeschaltet werden.
Sinnvoll ist noch das Unterbinden dieser Möglichkeiten im Parser (nicht fertig).

Verknüpfungs-Operatoren (Infix-Operators)

Achtung: Operator-Priorität (Vorrang, Punktrechnung vor Strichrechnung) wird beachtet.

String-Operatoren:
+ String-Verkettung

numerische Operatoren:
+
-
*
/
** ^ Potenz (Power)
% mod

Boolean-Operatoren:
and UND-Verknüpfung mit verkürzter Auswertung (Short-Circuit-Evaluation)
&& UND-Verknüpfung mit verkürzter Auswertung (Short-Circuit-Evaluation)
& UND-Verknüpfung ohne verkürzte Auswertung
or ODER-Verknüpfung mit verkürzter Auswertung (Short-Circuit-Evaluation)
|| ODER-Verknüpfung mit verkürzter Auswertung (Short-Circuit-Evaluation)
| ODER-Verknüpfung ohne verkürzte Auswertung
nand Negiertes UND mit verkürzter Auswertung
nor Negiertes ODER mit verkürzter Auswertung
xor Exclusiv-Oder (Entweder Oder) ohne verkürzte Auswertung
Verkürzte Auswertung bedeutet, dass beim Vorliegen ausreichender Werte (false beim UND-Operator, true beim ODER-Operator) die Abarbeitung des (Teil-) Ausdruckes abgebrochen wird, wodurch eine bessere Performance erreicht wird.
Falls es wichtig ist, dass alle Unterausdrücke abgearbeitet werden, damit entsprechende Seiteneffekte der Unterausdrücke wirken, müssen die Operatoren ohne verkürzte Auswertung verwendet werden.

Vergleichs-Operatoren:
==
<= =<
>= =>
!= <>

Bitweise Boolean-Operatoren habe ich nicht eingebaut, da diese sehr selten benötigt werden. Prinzipiell können diese aber zur Verfügung gestellt werden.

Zuweisungsoperatoren (Assignment-Operatoren)

:=  einfache Zuweisung
+=  Addititon und Zuweisung
-=  Subtraktion und Zuweisung
*=  Multiplikation und Zuweisung
/=  Division und Zuweisung
:>= Zuweisung wenn kleiner als (so was wollte ich schon immer)
:min=
:<= Zuweisung wenn grösser als (so was wollte ich schon immer)
:max=
Statt der Zuweisung kann man auch die setVar/setVarForName-Funktion nutzen.

Prefix-Operatoren

! logische Negation
- numerische Negation ( value * -1 )
@ Erzeugen einer Referenz (nicht fertig)

Inline-Operatoren

var++ Erhöhen der Variable um EINS nach der Abfrage
++var Erhöhen der Variable um EINS vor der Abfrage
var-- Verringern der Variable um EINS nach der Abfrage
--var Verringern der Variable um EINS vor der Abfrage

Idee:

Warum nur Incrementieren und Dekrementieren?
Man könnte sich alle möglichen Operationen Inline vorstellen.
var++2 (erhöhen um 2)
Das könnte man aber auch über Funktionen realisieren:

preSet( var , expression ) Variable setzen und neuen Wert zurückgeben ( ++i )
postSet( var , expression ) Variable setzen, aber alten Wert zurückgeben ( i++ )

Damit der Ausdruck nicht in einer endlosen Rekursion/Iteration scheitert:
preSetLater( var , expression ) neuen Wert zurückgeben, ( ++i )
Variable nach kompletter Evaluation des Ausdrucks setzen
postSetLater( var , expression ) alten Wert zurückgeben, ( i++ )
Variable nach kompletter Evaluation des Ausdrucks setzen
dagegen

preSetNow( var , expression ) Variable sofort setzen und neuen Wert zurückgeben ( ++i )
postSetNow( var , expression ) Variable sofort setzen, aber alten Wert zurückgeben ( i++ )

Die Funktionen preSet und postSet sollten sich demzufolge nach der allgemeinen Definition oder einem globalen Schalter richten.

Alternativ ist noch die Zuweisung im Ausdruck:

3 + ( a := a + 2 )

möglich.
Das erlaubt aber keine Steuerung des Rückspeicher-Zeitpunktes.

Ausserdem kann die Zuweisung mit Hilfe der Funktionen setVar, setVariable,
setVarForName und setVariableForName nachgebildet werden.

Operator-Priorität

05: Prefix- und Postfix-Operatoren: ++ -- -(als Vorzeichen) !
04: Multiplikation Division: * / %
03: Addition Subtraktion: + - +(String-Verkettung)
02: Vergleichsoperatoren: == <= =< >= => < > != <>
01: logische Operatoren: && || & |
00: Zuweisungs-Operatoren

Höherwertige Operatoren werden vor den niederwertigen ausgeführt.

Built-In-Funktionen

Expression-Funktionen:
-(compile( <valueString> )
Kompiliert den übergebenen String
-(
eval( <valueExpression/expressionString> )
Führt die übergebenene kompilierte Expression aus oder kompiliert und evaluiert den übergebenen String
-isValid oder isvalid
Prüft, ob der übergebene String ohne Fehler kompiliert werden kann
-evalType (nicht fertig)
Kompilieren und Ausführen des als String übergebenen Ausdruckes und zurückgeben des Types
Der ermittelte Typ wird als String zurückgegeben:
"NULL"
"STRING"
"NUMBER"
"BOOLEAN"
"EXPRESSION"
"DATE"
"STRINGARRAY"
"ARRAY"
"UNKNOWN"
-trycatch( <auszuführender Ausdruck> , <Ersatzwert, wenn gescheitert> )
Ausführung eines Ausdruckes mit try-catch-Abfangung und Lieferung des Ersatzwertes, wenn gescheitert
Variablen-Funktionen (RuntimeEnvironment):
-setVar( variable , <expression> )
-setVariable( variable , <expression> )
Setzt eine als literaler Variablen-Name zur Kompilationszeit bekannte Variable
-setVarForName( String varName , <expression> )
-setVariableForName( String varName , <expression> )
Setzt eine als Variablen-Namen-String zur Evaluationszeit bekannte Variable
-removeVar( variable )
-removeVarForName( String varName )
-varExists( <var> )
ob die übergebene Variable existiert
-varExistsForName( "<var>" )
ob die als String übergebene Variable existiert
-type
ermittelt den Typ des per Parameter übergebene Ausdrucks (NUMBER, BOOL, STRING, NULL, EXPRESSION, STRINGARRAY, ARRAY, UNKNOWN)
-isArray( <value> ) .
ermittelt, ob der übergebene Ausdrucks vom Typ Array ist
-isBool isBoolean isbool isboolean
prüft, ob ein Ausdruck vom Typ Boolean ist
-isExpression
ermittelt ob der per Parameter übergebene Ausdruck vom Typ Expression ist
-isNumber isnumber isNum isnum
prüft, ob ein Ausdruck vom Typ Number ist
-isString isstring isStr isstr
prüft, ob ein Ausdruck vom Typ String ist
-isNull oder isnull
ermittelt ob der per Parameter übergebene Ausdruck null ist
-empty
ermittelt ob der per Parameter übergebene Ausdruck null oder ein Leerstring (Länge 0)
oder eine leere Collection ( xxx.size() < 1 )
oder ein leeres Array ( xxx.length < 1 ) ist
bei Objekten einer anderen Klasse als String oder Collection wird immer false zurückgegeben
-isNullOrSpaceString oder isnullorspacestring
ermittelt ob der per Parameter übergebene Ausdruck null oder ein Leerstring (Länge 0) oder ein String aus WhiteSpaces (benutzt java.lang.String#trim() ) ist
bei Objekten einer anderen Klasse als String wird immer false zurückgegeben
-genRef oder getRef (nicht fertig)
Erzeugen einer Referenzvariable (Zeiger) auf eine Variable
Einbau entsprechender Erweiterungen in das RuntimeEnvironment

numerische Funktionen:
-num
wandelt einen String in einen Zahlenwert um
-mod modulo (nicht fertig)
-sqrt
liefert die Wurzel 2 des als Parameter übergebenen Wertes
-sin
Sinus des als Parameter übergebenen Wertes
-cos
Cosinus des als Parameter übergebenen Wertes
-abs
Absolutwert des als Parameter übergebenen Wertes
-acos
ArcusCosinus des als Parameter übergebenen Wertes
-asin
ArcusSinus des als Parameter übergebenen Wertes
-atan
ArcusTangus des als Parameter übergebenen Wertes
-asin
ArcusSinus des als Parameter übergebenen Wertes
-ceil
Aufrunden des als Parameter übergebenen Wertes
-exp
Exponential des als Parameter übergebenen Wertes
-floor
Abrunden des als Parameter übergebenen Wertes
-log
natürlicher Logarithmus (Basis Eulersche Zahl) des als Parameter übergebenen Wertes
-pow(n1,n2)
Potenzieren des als Parameter übergebenen Wertes
-round
Runden mit optionaler Angabe der Stellen hinter dem Komma
-random
Liefert eine Zufallszahl 0.0 bis 1.0
-tan
Tangus des als Parameter übergebenen Wertes
-toDegrees
Umwandlung des als Parameter übergebenen Wertes von Radiant zu Dezimalgrad
-toRadians
Umwandlung des als Parameter übergebenen Wertes von Dezimalgrad zu Radiant
-str
wandelt einen numerischen Wert in einen String um (noch optionale Angabe der Dezimalstellen einbauen)
-strzero( number , anzahl stellen ) oder strZero( number , anzahl stellen )
wandelt einen numerischen Wert in einen String mit führenden Nullen um (nicht fertig)
-reciproc (1/x)
liefert das Reziprok (1/x) des übergebenen Parameters
-min( valueComparable1 , valueComparable2 ) (geeignet fuer alle Comparable)
liefert das Minimum zweier als Parameter übergebenen Werte
-max( valueComparable1 , valueComparable2 ) (geeignet fuer alle Comparable)
liefert das Maximum zweier als Parameter übergebenen Werte

String-Funktionen
-substring wie Java-String.substring()
substring( string , beginIndex(base0) [ . endIndex(base0) ] )
-subs1
Positionierung auf Basis Eins, gibt bei Scheitern Leerstring zurück (nicht fertig)
subs1( anfangsposition , anzahl ) (nicht fertig)
subs1( anfangsposition ) (nicht fertig)
-subs0
Positioniereung auf Basis Null, gibt bei Scheitern Leerstring zurück (nicht fertig)
subs0( anfangsposition , anzahl ) (nicht fertig)
subs0( anfangsposition ) (nicht fertig)

-left( string, numCount )
liefert numCount Zeichen des Strings von links beginnend
-right( string, numCount )
liefert numCount Zeichen des Strings von rechts beginnend

-at pos indexOf
indexOf( <valueString> , <searchString> [ , <endIndex(base0)> ] )
-rightAt lastPos lastIndexOf
lastIndexOf( <valueString> , <searchString> [ , <endIndex(base0)> ] )
-contents( valueString , suchString [ startPos(base) ] )
liefert zurück, ob suchString in valueString enthalten ist
-contentsIgnoreCase
Beachten der Locale der JVM
-upper
-upper1 capitalize
ersten Buchstaben eines Strings in Grossbuchstraben umwandeln (noch nicht fertig)
-lower
-lower1
ersten Buchstaben eines Strings in Kleinbuchstraben umwandeln (noch nicht fertig)
-pad oder padr
verlängert den als Parameter übergebenen String auf die Länge numCount
durch Auffüllen mit Leerzeichen am rechten Rand

-padl
verlängert den als Parameter übergebenen String auf die Länge numCount
durch Auffüllen mit Leerzeichen am linken Rand

-spaces
liefert einen String aus Leerzeichen in der gewünschten Länge
-length( <value> )
Länge eines Strings, Arrays oder einer Collection
-charCode oder asc( string )
liefert den numerischen Code des ersten Zeichens des übergebene Strings
-char oder chr( charCode )
liefert einen String der Länge 1 mit dem Character, dessen Code der übergebenen Zahl entspricht

-startsWith( string , startString [ , beginPos(base0) ] )
-endsWith( string , endString )
-replicate( string , numCount )
wiederholt einen String in der angegebenen Anzahl
-trim( string )
Entfernen von WhiteSpaces an beiden Enden des Strings
-ltrim( string )
Entfernen von WhiteSpaces am linken Ende des Strings (nicht ferig)
-rtrim( string )
Entfernen von WhiteSpaces am rechten Ende des Strings (nicht ferig)

Date-Funktionen
-strToDate Parsen eines String 'dd.MM.yyyy' in ein java.util.Date oder null, wenn nicht korrekt
-timeOfDay Ermitteln der Uhrzeit aus einem Datum
-dayOfWeek Ermitteln des Wochentages aus einem Datum
-month Ermitteln des Monates aus einem Datum
-year Ermitteln des Jahres aus einem Datum

Boolean-Funktionen / Programmfluss
if iif
- if( <booleanExpression> , <trueExpression> , <falseExpression> )
-iif( <booleanExpression> , <trueExpression> , <falseExpression> )
mit verkürzter Auswertung (Short-Circuit-Evaluation)

-while( bedingung , bodyExpression/Sequenz )
Ausführung der Body-Expression solange der erste Parameter true ist
-repeat( bedingung , bodyExpression/Sequenz )
Ausführung der Body-Expression solange der erste Parameter true ist, aber mindestens einmal
-for( startExpression , repeatCondition , stepExpresion , bodyExpression )
for( i := 0 , i <= 3 , i++ , { print( 'for:' ) ; println( i ) }
-assert( <booleanExpresseion> , <Error-String> )
Ermittelt ob der per Parameter übergebene Ausdruck vom Typ Boolean 'true' ist.
Wenn nicht wird eine Exception mit dem übergebenen Fehlertext geworfen.

Array-Funktionen
newStringArray( <numSize> )
newStringArray( <str[0]> [ , <str[n]> ] )
Erzeugt ein neues String-Array in der verlangten Grösse (numerischer Parameter) oder aus der übergebenen String-Parameter-Liste.
asList( <array> ) arrayToList( <array> )
Wandelt ein Array in eine java.util.ArrayList um.
asListNotNull( <array> ) arrayToListNotNull( <array> )
Wandelt ein Array in eine java.util.ArrayList um. Statt null wird eine leere ArrayList zurückgegeben.
emptyList( )
Liefert eine leere java.util.ArrayList.

Ausgabe-Funktionen
-print( <expression> ) oder outPrint( <expression> )
Ausgabe des als Parameter übergebenen Ausdruckes auf System.out ohne Zeilenumbruch
Zurückgegeben wird der ausgegebene Ausdruck.
-println( <expression> ) oder outPrintln( <expression> )
Ausgabe des als Parameter übergebenen Ausdruckes auf System.out mit Zeilenumbruch
Zurückgegeben wird der ausgegebene Ausdruck.
-errPrint (nicht fertig)
-errPrintln (nicht fertig)

Collections-Funktionen
-length( <value> )
Länge eines Strings, Arrays oder einer Collection
-add( <List or Array> , <value> [ , <index> ] )
Anfügen eines Wertes an eine java.util.List oder eine Array
-removeAt( <List or Array> , <value> [ , <index> ] )
Entfernen eines Wertes aus einer java.util.List oder einem Array an der übergebenen Position

Object(Reflection)-Funktionen
-newObject( <ClassName> )
Erzeugen eines beliebigen Objektes mit dem paramterlosen Konstruktor
-getProperty( <object> , <propertyName (String)> )
Abfragen eines Public-Feldes oder Aufruf der entsprechenden Public-getXy()-Methode per Reflection
-setProperty( <object> , <propertyName (String)> , <value> )
Setzen eines Public-Feldes oder Aufruf der entsprechenden Public-setXy()-Methode per Reflection
-invokeMethod( <object> , <methodName (String)> [ , <value/values> ] )
Aufruf der spezifizierten Methode auf dem übergebenen Objekt mit den übergebenen Parametern per Reflection

Die fehlenden und eventuell weitere nicht mit aufgelistete Funktionen werde ich nach und nach Fertigstellen, da dies eine reine Fleissarbeit ist.
Beispiele für die Anwendung der Funktionen finden sich in der main-Methode der Klasse Expression.

Verarbeitung von Expression Language Strings

Die in der JSP Standard Tag Lib realisierte Expression Language arbeitet mit Strings in der Art

"abc${var1}def"

wobei im Bereich ${ ... } ein zur Laufzeit auszuwertender Ausdruck stehen kann.

Dem will ich mich nicht verschliessen.

Also habe ich einen entsprechenden Konverter in meine Expression Lib eingebaut:

Methode Expression.convertExpLangToExp( String )

Zum Parsen eines Strings mit Expressions der Expression Language dient die Methode:

Expression.parseExpLang( String )

Einbau eigener Funktionen

Anlegen einer Klasse im Package de.cnc.expression.functions analog zur Klasse AbsolutFunction

Erweitern der Parser-Methode AbstractFunction#parse

Typ-Prüfung:

Einbau eigener Operatoren

Anlegen einer Klasse im Package de.cnc.expression.assignmentoperators oder de.cnc.expression.infixoperators oder de.cnc.expression.prefixoperators analog zur Klasse SetOperator oder PlusOperator oder NotPrefixOperator

Erweitern der Parser-Methode AbstractAssignmentOperator#parseAssignmentOperator oder AbstractInfixOperator#parseInfixOperator oder AbstractPrefixOperator#parsePrefixOperator

Eintragen in die Prüfroutine AbstractInfixOperator#isOperator

CnC Template Engine

Auf der Grundlage des Expression Parsers ist es nun relativ naheliegend eine Template Engine zu bauen.

Aus Mangel an eigenen Ideen benutze ich die JSP-Notation:

  <% ...scriptlet code... %>

  <%= ...scriptlet expression... %>
Übersetzt wird ein Template in Java mit der statischen Methode
  public static String compileTemplate(String);
oder in der Expression-Engine mit der Built-In-Function
  compileTemplate(String)
Beim Übersetzen und Ausführen des Templates wird die reservierte Variable CNC_TMPL_OUT vrwendet. Solange keine Sequence- lokalen Variablen realisiert sind, ist es nicht möglich, Templates rekursiv geschachtelt auszuführen.

An den Beispielen auf der Online-Demo sieht man, dass es besonders wichtig ist, dass mehrfach geschachtelte Scriptlets durch zusätzliche symmetrische Curly Braces { ...sequence... } eingeschlossen werden.
  aaa <% for( i := 0 , i <= 3 , i++ , { %>bbb <% } ) ;%>ccc
                                      ^          ^

Neuerungen ab August 2005

Methoden isConst und getConstVal zum Festellen, ob ein Ausdruck konstant ist und zum Abfragen des konstanten Wertes

Typprüfung bei getCheckReturnTypes wird ein runtimeenvironment mitgegeben,
Befehl var zum deklarieren einer Variablen mit Typ(Klasse) im runtimeenvironment

Typprüfung auf Methoden und Properties


Test und Download

Online-Tester für den Expression-Parser auf www.control-and-command.de .
Bitte im Hauptmenü den Link zum Expression-Parser wählen (relativ weit unten).
Aus Zeitgründen habe ich für den Tester kein eigenes Login programmiert.

Download der Quelldateien Expression.zip

Zum Kompilieren und Starten müssen noch die Sourcen der SharedUtil's eingebunden werden.

Achtung: Erweiterungen und Fixes stelle ich ohne Historie und ohne Ankündigung hier bereit.
Deshalb am besten immer die letzte Version runterladen.

Lizenzbedingungen:

Die Programme, Quelltexte und Dokumentationen können ohne irgendwelche Bedingungen kostenlos verwendet werden.
Sie sind Freeware und Open Source. Für Fehler und Folgen wird keinerlei Haftung übernommen.

Hinweise zur Fehlerbeseitigung und Verbesserung sind mir willkommen.

Ich freue mich auch über Feedback bezüglich der erfolgreichen Verwendung meiner Sourcen.

Bei Fragen helfe ich gern mit Hinweisen oder zusätzlicher Dokumentation, falls ich dafür Zeit habe.