Gunnar Bittersmann: Suche eine Regex

Beitrag lesen

@@Rolf b

Ich möchte diesen String parsen und bei Syntaxfehlern halbwegs exakt angeben können, wo der Fehler ist.

Ich bin mir nicht sicher, ob reguläre Ausdrücke hierfür das geeignete Werkzeug sind.

Ein einzeiliger String enthält 1-n Regeln. … Regeln sind durch Komma getrennt. Die Kommas dürfen von Leerstellen umgeben sein.

[1] ⟨String⟩ ::= ⟨Regel⟩(\s*,\s*⟨Regel⟩)*

Eine Regel besteht aus einer Source-Referenz, gefolgt von einem Vergleichsoperator, gefolgt von einem Vergleichswert.

[2] ⟨Regel⟩ ::= ⟨SourceReferenz⟩⟨Vergleichsoperator⟩⟨Vergleichswert⟩

Eine Source-Referenz ist ein einfacher Name. Er kann aus ASCII-Buchstaben und Ziffern bestehen, darf aber nicht mit einer Ziffer beginnen. Ja, ich weiß, es gibt UNICODE. Hier brauche ich aber ASCII. (?<dataRef>[a-zA-Z][a-zA-Z0-9]*)

[3] ⟨SourceReferenz⟩ ::= [a-zA-Z][a-zA-Z0-9]*

Vergleichsoperator ist =, !=, <, >, <=, >= oder LIKE. Die Vergleichsoperatoren können von Leerstellen umgeben sein, LIKE muss mindestens eine Leerstelle vor und hinter sich haben.

[4] ⟨Vergleichsoperator⟩ ::= \s*(=|!=|<|>|<=|>=|\sLIKE\s)\s*

Hier ließe sich auch zu \s*(!?=|[<>]=?|\sLIKE\s)\s* zusammenfassen, aber das macht den Ausdruck nicht besser lesbar.

Ein Vergleichswert ist ein einfacher Wert oder eine Werteliste.

[5] ⟨Vergleichswert⟩ ::= (⟨einfacherWert⟩|⟨Werteliste⟩)

Ein einfacher Wert kann eins von drei Dingen sein:

[6] ⟨einfacherWert⟩ ::= (⟨Integerzahl⟩|⟨Stringkonstante⟩|⟨Variablenreferenz⟩)
  • Eine Integerzahl
[7] ⟨Integerzahl⟩ ::= [+-]?[0-9]+

Plus ist erlaubt? Führende Nullen sind erlaubt?

  • Eine Stringkonstante, in Hochkomma eingeschlossen. Hochkomma innnerhalb des Strings werden durch ein \ escaped. Innerhalb des Strings ist alles erlaubt (außer Zeilenumbrüchen...).
[8] ⟨Stringkonstante⟩ ::= '([^']|\\')*'
  • Eine Variablenreferenz. Das ist ein oder zwei $, gefolgt von einer Datenreferenz.
[9] ⟨Variablenreferenz⟩ ::= \$\$?⟨Datenreferenz⟩

Eine Datenreferenz kann eins von dreien sein:

  • sie ist leer ("$" ist eine zulässige Variablenreferenz)
  • ein erweiterter Name, bestehend aus Buchstaben, Ziffern, Minuszeichen und Unterstrich
  • Eine Datenreferenz, gefolgt von einem Punkt, gefolgt von einem erweiterten Namen.
[10] ⟨erweiterterName⟩ ::= [a-zA-Z0-9_-]+
[11] ⟨leerOderErweiterterName⟩ ::= [a-zA-Z0-9_-]*

Ein erweiterter Name muss nicht mit einem Buchstaben beginnen?

[12] ⟨Datenreferenz⟩ ::= ((⟨leerOderErweiterterName⟩\.)*⟨erweiterterName⟩)?

Eine Werteliste besteht aus einer linken Klammer, einer beliebigen Menge von einfachen Werten, die durch Komma getrennt sind, und einer rechten Klammer. Die Kommas dürfen von Leerstellen umgeben sein.

[13] ⟨Werteliste⟩ ::= \(⟨einfacherWert⟩(\s*,\s*⟨einfacherWert⟩)*\)

Das musst du jetzt nur noch™ alles zusammensetzen:

[10] und [11] in [12] eingesetzt ergibt:

[14] ⟨Datenreferenz⟩ = (([a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_-]+)?

[14] in [9]:

[15] ⟨Variablenreferenz⟩ = \$\$?(([a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_-]+)?

[7], [8], [15] in [6]:

[16] ⟨einfacherWert⟩ = ([+-]?[0-9]+|'([^']|\\')*'|\$\$?(([a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_-]+)?)

[16] in [13]:

[17] ⟨Werteliste⟩ = \(([+-]?[0-9]+|'([^']|\\')*'|\$\$?(([a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_-]+)?)(\s*,\s*([+-]?[0-9]+|'([^']|\\')*'|\$\$?(([a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_-]+)?))*\)

[16], [17] in [5]:

[18] ⟨Vergleichswert⟩ = (([+-]?[0-9]+|'([^']|\\')*'|\$\$?(([a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_-]+)?)|\(([+-]?[0-9]+|'([^']|\\')*'|\$\$?(([a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_-]+)?)(\s*,\s*([+-]?[0-9]+|'([^']|\\')*'|\$\$?(([a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_-]+)?))*\))

[3], [4], [18] in [2]:

[19] ⟨Regel⟩ = [a-zA-Z][a-zA-Z0-9]*\s*(=|!=|<|>|<=|>=|\sLIKE\s)\s*(([+-]?[0-9]+|'([^']|\\')*'|\$\$?(([a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_-]+)?)|\(([+-]?[0-9]+|'([^']|\\')*'|\$\$?(([a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_-]+)?)(\s*,\s*([+-]?[0-9]+|'([^']|\\')*'|\$\$?(([a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_-]+)?))*\))

[19] in [1]:

[20] ⟨String⟩ = [a-zA-Z][a-zA-Z0-9]*\s*(=|!=|<|>|<=|>=|\sLIKE\s)\s*(([+-]?[0-9]+|'([^']|\\')*'|\$\$?(([a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_-]+)?)|\(([+-]?[0-9]+|'([^']|\\')*'|\$\$?(([a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_-]+)?)(\s*,\s*([+-]?[0-9]+|'([^']|\\')*'|\$\$?(([a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_-]+)?))*\))(\s*,\s*[a-zA-Z][a-zA-Z0-9]*\s*(=|!=|<|>|<=|>=|\sLIKE\s)\s*(([+-]?[0-9]+|'([^']|\\')*'|\$\$?(([a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_-]+)?)|\(([+-]?[0-9]+|'([^']|\\')*'|\$\$?(([a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_-]+)?)(\s*,\s*([+-]?[0-9]+|'([^']|\\')*'|\$\$?(([a-zA-Z0-9_-]*\.)*[a-zA-Z0-9_-]+)?))*\)))*

Bei konkreten Implementationen müssen ggfs. noch Zeichen escapet werden und wenn Teile nicht gemerkt werden sollen, wäre (?: statt ( zu schreiben – außer natürlich bei \(.

LLAP 🖖

--
“When UX doesn’t consider all users, shouldn’t it be known as ‘Some User Experience’ or... SUX? #a11y” —Billy Gregory