ACHTUNG. Das ist ein Archiv des alten forum.ruby-portal.de. Die aktuelle Mailingliste gibt es auf lists.ruby-lang.org/pipermail/ruby-de.

NOTICE. This is a ready-only copy of the old forum.ruby-portal.de. You can find the current mailing list at lists.ruby-lang.org/pipermail/ruby-de.

Die Programmiersprache Ruby

Blog|

Forum|

Wiki  


Alle Zeiten sind UTC + 1 Stunde [ Sommerzeit ]

Ein neues Thema erstellen Auf das Thema antworten  [ 9 Beiträge ] 
Autor Nachricht
BeitragVerfasst: 24 Jul 2005, 23:50 
Offline
Interpreter

Registriert: 15 Mär 2005, 19:26
Beiträge: 6142
Wohnort: Karlsruhe
Dies ist der erste Thread von mehreren mit der Überschrift WoNáDos Vergewaltigungen Regulärer Ausdrücke - Fall i. Wie daran schon erkennbar handeln sie alle von Regulären Ausdrücken und deren Anwendung in Ruby.

Ich hoffe, dass es für irgendjemanden da draussen in der wilden Internet-Welt nützlich sein wird.

Ohne viel Vorrede erst einmal den Code und die zugehörige Ausgabe. Erklärungen folgen direkt anschliessend.


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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
puts "***** Start der Tests, benoetigt Onigurama-Regex-Engine *****"

module Matchelements
def bal(lpar='(', rpar=')')
# In dieser Version k��nnen noch nicht alle Zeichen als Klammersymbole benutzt werden, da wegen der
# Benutzung des Escape-Symbols '\' beispielsweise 'w' zu '\w' werden w��rde. Das muss noch
# eingearbeitet werden. Wolfgang Nadasi-Donner - 2005.07.24
raise RegexpError,
"wrong length of left bracket '#{lpar}' in bal" unless lpar.length == 1
raise RegexpError,
"wrong length of right bracket '#{rpar}' in bal" unless rpar.length == 1
raise RegexpError,
"identical left and right bracket '#{lpar}' in bal" if lpar.eql?(rpar)
lclass, rclass = lpar, rpar
lclass = '\\' + lclass if lclass.match(/[\-\[\]]/)
rclass = '\\' + rclass if rclass.match(/[\-\[\]]/)
return "(?<bal>" +
"[^#{lclass}#{rclass}]*?" +
"(?:" +
"(?:" + # +++++ tempor��rer Mustertrick +++++
"x|" + # Dieser Trick ist wegen eines Onigurama-Fehlers
"(?:" + # eingebaut. Onigurama ist inzwischen korrigiert,
"\\#{lpar}\\g<bal>\\#{rpar}" + # so dass die trickfreie Version benutzt werden kann,
")" + # sobald die Onigurama-Version in Ruby integriert ist.
")" + # Trickfrei:
"[^#{lclass}#{rclass}]*?" + # (?:\\#{lpar}\\g<bal>\\#{rpar}[^#{lclass}#{rclass}]*?)*?
")*?" + # +++++ Ende des tempor��ren Mustertricks +++++
")"
end
end

include Matchelements

puts "\n***** Basistests *****"

orgstring= "5-((3+4)*5)+6+xx"
pattern = /^#{bal()}$/

puts "\nMuster: #{pattern.inspect}\nString: '#{orgstring}'"
if (res = orgstring.match(pattern)) then
puts "Match: '#{res}'"
else
puts "+++++ No Match"
end

orgstring= "5-[[3+4]*5]+6+xx"
pattern = /^#{bal('[',']')}$/

puts "\nMuster: #{pattern.inspect}\nString: '#{orgstring}'"
if (res = orgstring.match(pattern)) then
puts "Match: '#{res}'"
else
puts "+++++ No Match"
end

orgstring= "5-[[3+4}*5}+6+xx"
pattern = /^#{bal('[','}')}$/

puts "\nMuster: #{pattern.inspect}\nString: '#{orgstring}'"
if (res = orgstring.match(pattern)) then
puts "Match: '#{res}'"
else
puts "+++++ No Match"
end

puts "\n***** Nun die Anwendung *****"

orgstring= <<EOT
firstproc(p1,p2,p3(p31,p32),p4(p41,p42(p421,p422),p43),p5)
nochneproc ( einfach, nur , ein, paar, parameter )
exotischeproc ( (((param))), (i*(k-5)) )
dieletzte ( auch, mit ( fast ) , allem )

EOT


pattern1 = /(?<n>\w+)\s*(?<p>\(#{bal()}\))/
pattern2 = /[(,]\s*#{bal()}(?=\s*[,)])/

puts "***** Die Eingabe *****"
puts ''
orgstring.each {|line|puts line}
puts "\n***** Die Aufbereitung *****"
orgstring.scan(pattern1) do
puts "\n---- Name: '#{$~[1]}'"
i=1
$~[2].scan(pattern2) do
puts "Parameter #{i}: '#{$~[1]}'"
i+=1
end
end

puts "\n***** Ende der Tests *****"

Die Ausführung dieses Programms ergibt auf der Konsole als Ausgabe

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
47
48
49
50
***** Start der Tests, benoetigt Onigurama-Regex-Engine *****

***** Basistests *****

Muster: /^(?<bal>[^()]*?(?:(?:x|(?:\(\g<bal>\)))[^()]*?)*?)$/
String: '5-((3+4)*5)+6+xx'
Match: '5-((3+4)*5)+6+xx'

Muster: /^(?<bal>[^\]\]]*?(?:(?:x|(?:\[\g<bal>\]))[^\]\]]*?)*?)$/
String: '5-[[3+4]*5]+6+xx'
Match: '5-[[3+4]*5]+6+xx'

Muster: /^(?<bal>[^\]}]*?(?:(?:x|(?:\[\g<bal>\}))[^\]}]*?)*?)$/
String: '5-[[3+4}*5}+6+xx'
Match: '5-[[3+4}*5}+6+xx'

***** Nun die Anwendung *****
***** Die Eingabe *****

firstproc(p1,p2,p3(p31,p32),p4(p41,p42(p421,p422),p43),p5)
nochneproc ( einfach, nur , ein, paar, parameter )
exotischeproc ( (((param))), (i*(k-5)) )
dieletzte ( auch, mit ( fast ) , allem )

***** Die Aufbereitung *****

---- Name: 'firstproc'
Parameter 1: 'p1'
Parameter 2: 'p2'
Parameter 3: 'p3(p31,p32)'
Parameter 4: 'p4(p41,p42(p421,p422),p43)'
Parameter 5: 'p5'

---- Name: 'nochneproc'
Parameter 1: 'einfach'
Parameter 2: 'nur'
Parameter 3: 'ein'
Parameter 4: 'paar'
Parameter 5: 'parameter'

---- Name: 'exotischeproc'
Parameter 1: '(((param)))'
Parameter 2: '(i*(k-5))'

---- Name: 'dieletzte'
Parameter 1: 'auch'
Parameter 2: 'mit ( fast )'
Parameter 3: 'allem'

***** Ende der Tests *****

Dieser Code bedarf wohl einiger Erklärungen bevor jemand die relevanten Teile nutzen kann.

Zuerst einmal gibt es das Modul Matchelements, welches Methodendefinitionen enthält (hier nur bal) und später mittels include in der gewünschten Umgebung zur Verfügung gestellt wird.

Die hier definierten Methoden geben jeweils ein String-Objekt zurück, auf welches dann in Mustern über den Methodenaufruf zurückgegriffen werden kann (beispielsweise #{bal()}).

Diese Technik ist unabhängig von der Ruby-Version (also von der aktuellen Regex-Engine) und dient dazu übersichtlichere Muster zu konstruieren. Ausserdem können auf diese Weise ausgearbeitete Musterteile parametrisierbar wiederverwendet werden.

Nun zu dem bal-Beispiel.

Dieses benötigt die Onigurama-Regex-Engine, also Ruby 1.9 oder eine modifizierte Version 1.6/1.8, die dann selber zusammengestellt werden muss. Da Onigurama wesentlich mehr Musterelemente als die bisherige Mustermaschine von Ruby verarbeiten kann, sollte man sich die Beschreibung der Elemente herunterladen und ansehen. Man findet sie unter http://www.geocities.jp/kosako3/oniguruma/doc/RE.txt.

Für das im Modul definierte bal-Muster spielen drei Neuigkeiten eine Rolle.
  • Character Classes
    Innerhalb dieser Musterelemente müssen die Zeichen [, ] und - mittels des Escape-Zeichens \ dargestellt werden.
  • Named Groups
    Es können nicht nur durch Klammern die üblichen Gruppen \1, \2... definiert werden, sondern auch benannte Gruppen, die dann später über ihre Bezeichnung im Muster referiert werden können (beispielsweise \k<gruppe>).

    In Ruby gibt es diese Referenzmöglichkeit (noch) nicht. Sämtliche Matchergebnisse werden im MatchData-Objekt gehalten, und jenes kennt diese Techniken (noch) nicht. Ich habe durch Tests festgestellt, dass im MatchData-Objekt nur noch die benannten Gruppen gespeichert werden, sobald im Muster eine vorkommt. Die Gruppen werden nicht über ihre Bezeichner referiert, sondern über ihre Position im Muster.
  • Subexpression Call
    Erlaubt es benannte und unbenannte Gruppen innerhalb eines Muster aufzurufen - auch rekursiv(!).

    Achtung! - Diese Technik hat eigentlich mit regulären Ausdrücken nichts mehr zu tun. Es kann innerhalb von (meist schon unübersichtlichen) Musterausdrücken Recursive Descent Parsing durchgeführt werden, was auch nicht zu den einfachsten Dingen dieser Welt gehört. Die rekursive Benutzung dieser Technik sollte also nur wohlüberlegt und intensiv ausgearbeitet erfolgen.

    Im Beispiel wird diese Technik benutzt das Snobol4-Muster bal zu implementieren, welches (in etwa) .*? entspricht, nur dass die erkannte Zeichenfolge bezüglich der Klammerelemente ballanziert sein muss.

Ich möchte hier jetzt nicht einen Roman schreiben, also seitenlang die Methode Matchelements#bal erklären. Falls es jemand nutzen oder nur detailliert durcharbeiten will und irgendetwas unklar bleibt, sollte die zugehörige Frage hier im Thread gestellt werden.

PS: Einige Mini-Korrekturen durch Edit bis 2005.07.25 - 00:07

_________________
WoNáDo.set_state!(:retired)


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: 25 Jul 2005, 13:10 
Offline
Interpreter
Benutzeravatar

Registriert: 05 Jun 2005, 01:54
Beiträge: 3225
Respekt.

Und Vergewaltigung trifft vermutlich den Nagel auf den Kopf ;)

Darf ich fragen wozu du dass eigentlich brauchst? (Ich meine mich erinnern zu koennen dass du in comp.lang.ruby etwas ueber interaktive Datenanalyse gesagt hast)

Dein Bsp laesst naemlich fast auf einen Parser fuer eine Sprache schliessen...


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: 25 Jul 2005, 18:56 
Offline
Interpreter

Registriert: 15 Mär 2005, 19:26
Beiträge: 6142
Wohnort: Karlsruhe
cypher hat geschrieben:
... Darf ich fragen wozu du dass eigentlich brauchst? (Ich meine mich erinnern zu koennen dass du in comp.lang.ruby etwas ueber interaktive Datenanalyse gesagt hast) ...

Das ist eine seeeehhhhhr lange Story (sie begann 1978), deshalb nur den aktuellen Teil.

Zuerst eine Sache:
Ich will auf diese Art und Weise keinen Parser bauen und auch niemanden dazu bringen es zu tun - Das wäre eine völlig veraltete und unverständliche Methode!

Es dreht sich um ad hoc Datenanalysen und Umformungen (überwiegend interaktiv), sowie Programme, die nur ein paar Mal oder sehr selten benötigt werden. Es dreht sich also nicht um die Anwendungen, die Perl seinen Namen gaben, da Reports meist regelmässig erzeugt werden und die zugehörigen Programme schnell erstellt werden müssen und dann lange leben.

Hier ein paar Beispiele mit denen ich in den letzten Jahren zu tun hatte.
  • In einem schon länger laufenden Projekt gibt es hunderte Protokolle die mit dem Adobe Framemaker erstellt wurden und in denen im frei formatierten Text an diversen Stellen hinter bestimmten Überschriften Referenzen auf Modulnamen und Parameter stehen. Diese Daten werden benötigt. Der Framemaker ist für diese Arbeit nicht geeignet und es gibt keine Regeln für die Überschriften, so dass man mit vager Information auskommen und das zugrundeliegende Datenformat direkt bearbeiten muss. Das muss schnell gehen, 90%igkeit ist ausreichend und es wird nur einmal benötigt.
  • Eine Firma hat mehrere tausend Fehlertexte in einer Liste vorliegen, die ein Extrakt aus Definitionen in einer Programmiersprache sind. Aus den Variablennamen geht eine Fehlernummer hervor, zu der der Text gehört. Diese Liste wird nun in modifizierter Form für den Hotline-Support benötigt (Excel-Tabelle). Per Hand dauert es mehrere Tage, also ist ein schnell erstelltes Programm hilfreich, welches (mindestens) 95% der Fälle erledigt. Der Rest kann dann problemlos per Hand durchgeführt werden.
  • (Speziell zum bal-Muster) Im Bereich der Software-Maintenance (SWPÄ für VModell-Freunde :wink: ) bekommt man oft unzureichend dokumentierte Programme vorgesetzt, die ausserdem noch aus mehreren zehntausend/hunderttausend Zeilen Code bestehen. Um einen ersten Überblick zu erhalten ist es hilfreich sich die Aufrufe von Methoden/Funktionen/Unterprogrammen aufzulisten oder sogar einen Calltree zu erzeugen. Da oft ein Mischmasch aus verwendeten Programmiersprachen, die teils völlig veraltet sind vorliegt, kann man sich nur mit ad hoc erstellten Tools helfen. Dabei kommt es auf die Erstellungsgeschwindigkeit an und keinesfalls auf die runtime-Performance.
  • (Ein exotischer Fall) Es existiert eine Ahnendatenbank in die eine weitere Liste von Daten integriert werden soll. Diese zweite Liste ist sehr amateurhaft erstellt (manchmal Camelcase-artige Schreibweisen) und die verwendete Codierung kann auch nicht direkt verwendet werden.
Ich belasse es jetzt dabei, in der Realität waren es allein in den letzten 10 Jahren weit mehr Fälle dieser Art.

Das sind alles Fälle bei denen es Sinn macht sich an das gewünschte Ergebnis heranzutasten . Sobald man ein genügend genaues Resultat erhalten hat kann man den erstellten Code festhalten und das Ganze dann (sozusagen) im Batch-Modus durchlaufen lassen.

Ist der Anwendungsbereich jetzt etwas klarer geworden?

Dazu noch etwas gleichermassen Historisches wie Aktuelles. Es gab einmal an Telefunken TR440-Computern und an Siemens BS2000-Anlagen einen Editor namens Ediere. Dieser Editor wurde bei uns im GRZ (Grossrechenzentrum für die Wissenschaft in Berlin - aufgegangen im ZIB) in den Jahren 1975 bis 1980++ entwickelt. Er erlaubte es auf Dateien vorwärts und rückwärts zu arbeiten und musterorientiert zu suchen, ersetzen und in Zieldateien zu kopieren. Die Mustersprache orientierte sich an Snobol4 und nicht an regulären Ausdrücken, die damals wirklich noch überwiegend regulär, also nur so leistungsfähig wie im lex waren. Der Ediere konnte interaktiv oder mit Scripten im Batch benutzt werden.

Ich hatte damals mit der Konvertierung des SPSS-Statistikpakets auf den TR440 zu tun und war in das Software Adaption and Maintenance Organisation System-Projekt eingebunden. In diesem Rahmen stellte sich heraus, dass ein Tool wie der Ediere als Analyse-Werkzeug und für systematische Transformationen unglaublich mächtig ist.

Aus diesem Grunde haben Frank (zur Zeit im Forum aus Berufsgründen passiv) und ich angefangen einen modernen Ediere zu konzipieren und zu entwickeln. Dahinter steht dann das PopAnEd-Projekt (Problem Oriented Pattern Matching Analyser and Editor), sowie zahlreiche Experimente unter dem Namen Experidiere.

Ursprünglich wollten wir das in C# bauen, ich bin allerdings vor zwei Jahren auf dem Karlsruher Linux-Tag über Ruby gestolpert. Nun gab es da ein Problem, weil wir letztendlich unbedingt Unicode benötigen - da war die Unterstützung in Ruby noch (freundlich gesagt) rudimentär. Allerdings zogen mich sofort die Art der Iteratoren mit ihren Blöcken und die strenge OO-Orientiertheit magisch an.

Nun ergab sich dadurch, dass bei mir Kehlkopfkrebs diagnostiziert wurde die Situation, dass ich viel Zeit habe (Jobsuche entfällt längere Zeit und man sitzt bei Ärzten, Zuhause oder im Krankenhaus). Ich konnte mich also mal intensiv auf Ruby stürzen.

So etwas kommt dann dabei heraus...

_________________
WoNáDo.set_state!(:retired)


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: 25 Jul 2005, 20:32 
Offline
Böser Admin 2
Benutzeravatar

Registriert: 17 Mär 2004, 17:03
Beiträge: 2544
Wohnort: Berlin
danke für den einblick in die neuen möglichkeiten von Ruby 1.9!

das ist auf jeden fall nützlich. der große vorteil deiner bal-methode ist, dass man sie benutzen kann, ohne groß ahnung von regexps zu haben.
oder anders gesagt: man kann das WAS vom WIE trennen. typisch Ruby.

hier eine variante deines codes, gleichzeitig ein vorschlag für ein alternatives interface:

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
module Regexp::Tools
def bal delimiters
case delimiters.size
when 1
l = r = delimiters
when 2
l, r = delimiters.split('')
else
raise RegexpError, 'delimiters must be a one or two character string.'
end

l = Regexp.escape l
r = Regexp.escape r

/
(?<bal>
[^
#{l}#{r}]*?
(?:
(?:
x
|
(?:
#{l}\g<bal>#{r}
)
)
[^
#{l}#{r}]*?
)*?
)
/x

end

Regexp.extend self # it's Meta time!
end

puts "\n***** Basistests *****"

orgstring= "5-((3+4)*5)+6+xx"
pattern = /^#{Regexp.bal '()'}$/

puts "\nMuster: #{pattern.inspect}\nString: '#{orgstring}'"
if (res = orgstring.match(pattern)) then
puts "Match: '#{res}'"
else
puts "+++++ No Match"
end
(getestet mit ruby 1.9.0 (2004-05-19) [i386-mswin32])
insbesondere halte ich default-werte nicht für sinnvoll. gleiche klammern sollten dafür erlaubt sein.
Regexp.escape macht genau, was du brauchst. jetzt müsste auch w als klammer gehen.
als rückgabe von bal halte ich ein Regexp-objekt für sinnvoller, weil sich Ruby dann auch darum kümmert, die pattern-optionen zu erhalten.

was mir noch nicht ganz einleuchtet, ist die sache mit den quantifiern. die beispiele funktionieren nur mit *?, aber ich fand, dass man eigentlich gierige quantifier benutzen könnte (die sind ja auch schneller.)
vielleicht ein gieriges und ein nicht-gieriges bal?

wie war denn die SNOBOL-syntax für solche dinge? vielleicht kommen wir da noch näher heran, mit ein paar Ruby-tricks.


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: 25 Jul 2005, 21:06 
Offline
Interpreter

Registriert: 15 Mär 2005, 19:26
Beiträge: 6142
Wohnort: Karlsruhe
murphy hat geschrieben:
... insbesondere halte ich default-werte nicht für sinnvoll. gleiche klammern sollten dafür erlaubt sein.

Die Default-Werte waren nur wegen Snobol4, da es dort nur mit ( und ) ging. Mehr war allerdings nicht nötig, da man in der Sprache beliebig rekursive Muster über Variablenbezüge konstruieren konnte. Gleiche Klammern kann man natürlich zulassen. Es ergibt sich dann der Effekt, dass immer eine geradzahlige Anzahl von Klammern erlaubt sind.

Mein bal ist auch nur als Beispiel zum rumfummeln gedacht. Ich arbeite gerade an einer wesentlich allgemeineren Version, die dann auch längere Strings mit sogar überschneidenden Enden/Anfängen erlaubt.

murphy hat geschrieben:
... als rückgabe von bal halte ich ein Regexp-objekt für sinnvoller, weil sich Ruby dann auch darum kümmert, die pattern-optionen zu erhalten.

Klar, ich wollte aber auch eine Stringvariante haben. Eventuell will man so ein Modul ja nicht im fertigen Programm an den Stellen einsetzen wo ein Matching stattfindet (Performance), sondern damit die endgültig verwertbaren Regexes generieren um sie als String-Argument bei Regex.new einzusetzen. Man kann ja beide Varianten reinbringen.

murphy hat geschrieben:
... was mir noch nicht ganz einleuchtet, ist die sache mit den quantifiern. die beispiele funktionieren nur mit *?, aber ich fand, dass mein eigentlich gierige quntifier benutzen könnte (die sind ja auch schneller.)
vielleicht ein gieriges und ein nicht-gieriges bal?

Muss man überlegen wie man es braucht. Für mein Beispiel mit den Paramatern benötige ich die nicht-gierige Variante (probier mal aus). Es spricht ja nichts dagegen sich die Muster-Module nach Bedarf zurechtzubauen.

murphy hat geschrieben:
... wie war denn die SNOBOL-syntax für solche dinge? vielleicht kommen wir da noch näher ran mit ein paar Ruby-tricks.

Uff - ganz anders, es waren Funktionsaufrufe (siehe auch Icon-Programming-Language, ein Snobol-Nachfolger vom gleichen Autor, der die Namen beibehalten hat). In Snobol erweiterte bal (in dieser Form im Muster geschrieben) die Funktion arb (von "arbitrary" ~ beliebig), die entsprach genau *.?, derart, dass der erkannte Teil in Bezug auf ( und ) ballanziert sein musste.

Noch was zu Ruby und der neuen Mustermaschine.

Snobol4 ist heute in der Praxis kaum noch sinnvoll einsetzbar und Icon erfüllte für die hier im Thread genannten Anwendungen nicht vollständig die Erwartungen. Also kam ich irgendwann in den 90ern zu Perl.

Aaaaarrrggg - Ich bin heilfroh, dass es inzwischen Ruby gibt. Der Versuch derartige Sachen wie hier in Perl auf übersichtliche und nach einem halben Jahr auch noch verständliche Art und Weise zu konstruieren ist praktisch nicht möglich.

Die neue Mustermaschine mit diesen rekursiven Möglichkeiten ist wesentlich übersichtlicher als die entsprechende Lösung in .NET. Da wird mit impliziten Stacks gearbeitet, so dass man eine einmal geschriebene Lösung hinterher kaum noch versteht.

Ruby ist einfach der bessere Weg :D

Eines wäre natürlich noch schön ...

... wenn man die benannten Gruppen (?<name>...) in Regex-Objekten speichern könnte die über <name> ansprechbar wären, dann könnte man sich eine schöne Regex-Bibliothek zusammenbauen und auch Referenzen, rekursiv oder nicht, deutlich Übersichtlicher darstellen.

Schaumama...

_________________
WoNáDo.set_state!(:retired)


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: 28 Jul 2005, 22:15 
Offline
Interpreter

Registriert: 15 Mär 2005, 19:26
Beiträge: 6142
Wohnort: Karlsruhe
murphy hat geschrieben:
... Regexp.escape macht genau, was du brauchst. jetzt müsste auch w als klammer gehen.

Leider gibt es da ein paar Probleme. Es funktioniert in #{l}\g<bal>#{r}, jedoch nicht mehr in den Zeichenklassen [^#{l}#{r}].

Hier gelten andere Escaperegeln, ausserdem hat Onigurama noch ein paar Änderungen vorgenommen (siehe Beschreibung). Deshalb kommt man hier kaum um

1
2
3
    lclass, rclass = lpar, rpar
lclass = '\\' + lclass if lclass.match(/[\-\[\]\\]/)
rclass = '\\' + rclass if rclass.match(/[\-\[\]\\]/)
herum (im ersten Code fehlte übrigens noch \\)

_________________
WoNáDo.set_state!(:retired)


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: 30 Jul 2005, 20:40 
Offline
Interpreter

Registriert: 15 Mär 2005, 19:26
Beiträge: 6142
Wohnort: Karlsruhe
Bezuglich der Übersichtlichkeit und den erweiterten Möglichkeiten habe ich vorhin in comp.lang.ruby einen Beitrag geschrieben, der hoffentlich später zu einer Erweiterung der Klasse Regexp führen wird. Nur zur Information packe ich ihn hier als Anhang zum Thread.

[Brainstorming Input] Ruby-Oniguruma interoperability on Named Groups
Zitat:
Let me first explain the reason for and the kind of this message.

I have an vague idea on coming to more readable Regular Expression, and the possibility to build Libraries of
Regular Expressions. The hook are the named groups ('(?<name>...)') which are part of Oniguruma. The idea was
influenced by the ancient Snobol4-Language and its '*'-operator for unevaluated expressions.

This input is brainstorming material and not a change proposal, because it is not mature enough. I hope that
something like this will appear sometimes in the future in Ruby.

Now the idea.

Ruby and Onigurama should be extended somehow to allow Ruby-objects (usually regular expressions) to be
registered somehow to the class Regexp, so that they can be referenced later in regular expressions.

In detail a regular expression that consists only of a named group definition (starts with '(?<name>') kann be
registered by something like 'Regex.register(/(?<example>a|b|c|d)/)', and be deleted by
Regex.remove('<example>'). If the regular expression is assigned to a variable this can be used, how to manage
this in the 'remove' case has to be clearified. I used class methods for this example, but it may be better to
introduce a named Regexp objects which will be created by something like '/(?<example>a|b|c|d)/.create. Some
possibility for explicit deletion should be there, because the regex engine Oniguruma must know about the
object to take care about.

These Object can later on be referenced in regular expressions by '\k<name>' or '\g<name>' as if they were
defined there.

This could made regular expressions be much more readable, because one can build them based on smaller parts,
one can build special Libraries of regular expression parts that are usable in the applications, and one can
use regular expression parts that were build by others without complete understanding of their details.

I think that this is worth to think about.

Best regards, Wolfgang

_________________
WoNáDo.set_state!(:retired)


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: 01 Aug 2005, 01:29 
Offline
Böser Admin 2
Benutzeravatar

Registriert: 17 Mär 2004, 17:03
Beiträge: 2544
Wohnort: Berlin
eine variante des vorgeschlagenen verhaltens, die mit einfachen Rubymitteln funktioniert und meiner meinung nach einfacher und flexibler ist: konstanten, #{...} und /o:

1
2
3
4
5
6
IDENT = /[a-z_][\w_]*/i
METHOD_NAME = / #{IDENT} [?!]? /ox
INSTANCE_VARIABLE = / @ #{IDENT} /ox
CLASS_VARIABLE = / @@ #{IDENT} /ox
GLOBAL_VARIABLE = / \$ (?: #{IDENT} | [1-9] | 0[a-zA-Z_0-9]* | [~&+`'=\/,;_.<>!@$?*":\\] | -[a-zA-Z_0-9] ) /ox
VARIABLE = / @?@? #{IDENT} | #{GLOBAL_VARIABLE} /ox
es wäre nicht im sinne der modularisierung, wenn man etwas global im Regexp-objekt speichern würde (mein IDENT ist vielleicht nicht das IDENT einer anderen bibliothek usw.)

vielleicht habe ich die details noch nicht verstanden: was für einen vorteil bietet deine regexps-registry (RegExpReg? ;)) gegenüber diesem vorgehen?


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: 01 Aug 2005, 11:12 
Offline
Interpreter

Registriert: 15 Mär 2005, 19:26
Beiträge: 6142
Wohnort: Karlsruhe
murphy hat geschrieben:
... was für einen vorteil bietet deine regexps-registry (RegExpReg? ;)) gegenüber diesem vorgehen?

Den Vorteil, dass rekursiv gearbeitet werden kann. Die #{...}-Ersetzung ist, aus Sicht der Regulären Ausdrücke, etwas Ähnliches wie die Anwendung des C-Preprocessors - es geschieht zur Compile-Time der Ausdrücke. So etwas wie mypat = /a|b#{mypat}/ ginge nicht.

Die globale Speicherung war ja auch nur erwähnt um überhaupt etwas zu haben. Die hat sich längst durch Beiträge zu dem Thema erledigt.
Dominik Bathon hat geschrieben:
.. but I think registering all named groups in one global place is not a
good idea (even if you can unregister): what if two libraries use the same
group names? I think there would be many name clashes.

So here is another idea: Let the caller manage the named groups himself.
Maybe in arrays or hashes. Something like:

groups = [/(?<example>a|b|c|d)/, /(?<example2>e|f|g)/]

or with hashes:

groups = { "example" => /a|b|c|d/, "example2" => /e|f|g/ }

or maybe in some specialized named groups library class.

> These Object can later on be referenced in regular expressions by
> '\k<name>' or '\g<name>' as if they were
> defined there.

To use those groups I would suggest something like:

/\k<example>/.with(groups)

RegExp#with would return the "composed" RegExp that can be used like any
other RegExp.

What do you think?

Dominik

Disclaimer: I do not really know how named groups work in Oniguruma, just
wanted to point out that one global registry might be a bad idea.
Und meine Antwort
WoNáDo hat geschrieben:
First of all - I made a mistake. Please forget all '\k<name>...'-stuff. This is the same as '\1', '\2', ...,
which means, it is a reference to a match result of applying this group in the actual matching process. We are
talking here about the '\g<name>...' reference only, which is a call to the group during match time. For
simply prematch time replacement the '#{...}' Ruby construct is still usable.

It is clear for my understanding that in the Ruby environment the class 'Regexp' must be changed, as well as
'Oniguruma' itself, because it must be able to find the predefined patterns during a match process.

My suggestion based this on the prerequisite to have minimal changes in Oniguruma and Ruby's Regexp class -
making such changes acceptable and possible ;-) This implies not to change existing things in Ruby and
Oniguruma. Insofar I prefer the usage of '\g<name>' instead of some other notation, but that are only my
thoughts for it.

The idea of using hashes in Ruby and an extension of class Regexp having a 'with' method sounds very good.
This method is a candidate for building the connection to Oniguruma, which then knows where to search for a
'(?<paul>...)' expression, if it isn't defined in the actual regular expression, but referenced via '\g<paul>'
there.

The 'with' method may be able to have a list of hashes as parameter (or even multiple hashes as parameters),
because one may use more than one predefined pattern groups (may happen if one uses a general pattern library
and a special one for the application).

Man muss mal schauen ob überhaupt eine Bereitwilligkeit der zuständigen Leuts existiert so etwas einzubauen. Da die Veränderungen nicht umfangreich sind müsste es relativ unproblematisch gehen ...

... aber ...

... es wäre damit allerdings eine gegenseitige Kopplung von Ruby (Anwendung) und Oniguruma gegeben, egal hinter welchem Design-Pattern man das nun versteckt. Ob das erwünscht ist kann ich nicht einmal vermuten.

_________________
WoNáDo.set_state!(:retired)


Nach oben
 Profil  
 
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 9 Beiträge ] 

Alle Zeiten sind UTC + 1 Stunde [ Sommerzeit ]


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 2 Gäste


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach: