Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Duden/Uri #100

Merged
merged 11 commits into from
Nov 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Der Changelog von DDP. Sortiert nach Release.

## In Entwicklung

- [Added] Duden/Uri
- [Fix] Der "als" Operator kann nun für verschiedene Rückgabetypen überladen werden
- [Fix] Man kann eine Variable, die eine andere überschreibt jetzt mit dieser initialisieren
- [Added] Man kann jetzt (auch rekursiv) alle Module aus einem Ordner einbinden
Expand Down
12 changes: 6 additions & 6 deletions lib/stdlib/Duden/Regex.ddp
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,27 @@ und kann so benutzt werden:
Die öffentliche Funktion Regex_Erster_Treffer mit den Parametern muster und text vom Typ Text und Text, gibt einen Treffer zurück,
ist in "libddpstdlib.a" definiert
und kann so benutzt werden:
"der erster Treffer von <muster> auf <text>"
"der erste Treffer von <muster> in <text>"

Die öffentliche Funktion Regex_N_Treffer mit den Parametern muster, text und n vom Typ Text, Text und Zahl, gibt eine Treffer Liste zurück,
ist in "libddpstdlib.a" definiert
und kann so benutzt werden:
"die ersten <n> Treffer von <muster> auf <text>"
"die ersten <n> Treffer von <muster> in <text>"

Die öffentliche Funktion Regex_Alle_Treffer mit den Parametern muster und text vom Typ Text und Text, gibt eine Treffer Liste zurück, macht:
Gib (die ersten -1 Treffer von muster auf text) zurück.
Gib (die ersten -1 Treffer von muster in text) zurück.
Und kann so benutzt werden:
"alle Treffer <muster> <text>"
"alle Treffer von <muster> in <text>"

Die öffentliche Funktion Regex_Erster_Treffer_Ersetzen mit den Parametern muster, text und ersatz vom Typ Text, Text und Text, gibt einen Text zurück,
ist in "libddpstdlib.a" definiert
und kann so benutzt werden:
"den ersten Treffer mit dem Regex <muster> auf <text> mit <ersatz> ersetzt"
"den ersten Treffer mit dem Regex <muster> in <text> mit <ersatz> ersetzt"

Die öffentliche Funktion Regex_Alle_Treffer_Ersetzen mit den Parametern muster, text und ersatz vom Typ Text, Text und Text, gibt einen Text zurück,
ist in "libddpstdlib.a" definiert
und kann so benutzt werden:
"alle Treffer mit dem Regex <muster> auf <text> mit <ersatz> ersetzt"
"alle Treffer mit dem Regex <muster> in <text> mit <ersatz> ersetzt"

Die öffentliche Funktion Regex_Spalten mit den Parametern muster und text vom Typ Text und Text, gibt eine Text Liste zurück,
ist in "libddpstdlib.a" definiert
Expand Down
182 changes: 182 additions & 0 deletions lib/stdlib/Duden/Uri.ddp
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
Binde "Duden/Regex" ein.
Binde "Duden/Texte" ein.
Binde "Duden/Fehlerbehandlung" ein.

Wir nennen die öffentliche Kombination aus
dem Text RoheUri mit Standardwert "",
dem öffentlichen Text Schema mit Standardwert "",
dem öffentlichen Text Nutzer mit Standardwert "",
dem öffentlichen Text Host mit Standardwert "",
dem öffentlichen Text Port mit Standardwert "",
dem öffentlichen Text Pfad mit Standardwert "",
dem öffentlichen Text Abfrage mit Standardwert "",
dem öffentlichen Text Fragment mit Standardwert "",
eine Uri,
und erstellen sie so:
"eine leere Uri"

Der Text uri_regex ist "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?".
Der Text authority_regex ist "^((.+)@)?((\\[[:0-9A-Fa-f.]+\\])|([a-zA-Z0-9\\-\\._~!$&'\\(\\)\\*\\+,;=])*)(:(\\d+))?".
Die öffentliche Funktion Parse_Uri mit dem Parameter uri vom Typ Text, gibt eine Uri zurück, macht:
Diese Funktion könnte einen Fehler melden.
Wenn uri leer ist, gib eine leere Uri zurück.

Der Treffer t ist der erste Treffer von uri_regex in uri.
Wenn es dabei einen Fehler gab oder (text von t) leer ist,
gib eine leere Uri zurück.

Die Uri ergebnis ist eine leere Uri.
Speichere text von t in RoheUri von ergebnis.
Speichere gruppen von t an der Stelle 2 in Schema von ergebnis.

[authority]
Wenn (gruppen von t an der Stelle 4) nicht leer ist, dann:
Der Treffer t ist der erste Treffer von authority_regex in (gruppen von t an der Stelle 4).
Wenn es dabei keinen Fehler gab und (text von t) nicht leer ist, dann:
Speichere gruppen von t an der Stelle 2 in Nutzer von ergebnis.
Speichere gruppen von t an der Stelle 3 in Host von ergebnis.
Speichere gruppen von t an der Stelle 7 in Port von ergebnis.

Speichere gruppen von t an der Stelle 5 in Pfad von ergebnis.
Speichere gruppen von t an der Stelle 7 in Abfrage von ergebnis.
Speichere gruppen von t an der Stelle 9 in Fragment von ergebnis.

Gib ergebnis zurück.
Und überlädt den "als" Operator.

Die öffentliche Funktion Uri_Zu_Text mit dem Parameter uri vom Typ Uri, gibt einen Text zurück, macht:
[ TODO: durch TextBauer ersetzen ]
Der Text t ist "".

Wenn (Schema von uri) nicht leer ist, dann:
Füge (Schema von uri) an t an.
Füge ':' an t an.

[URN edge case]
Wenn (Host von uri) nicht leer ist oder '/' am Anfang von (Pfad von uri) steht, dann:
Füge "//" an t an.

Wenn (Host von uri) nicht leer ist, dann:
Wenn (Nutzer von uri) nicht leer ist, dann:
Füge (Nutzer von uri) an t an.
Füge '@' an t an.
Füge (Host von uri) an t an.
Wenn (Port von uri) nicht leer ist, dann:
Füge ':' an t an.
Füge (Port von uri) an t an.

Füge (Pfad von uri) an t an.

Wenn (Abfrage von uri) nicht leer ist, dann:
Füge '?' an t an.
Füge (Abfrage von uri) an t an.

Wenn (Fragment von uri) nicht leer ist, dann:
Füge '#' an t an.
Füge (Fragment von uri) an t an.

Gib t zurück.
Und überlädt den "als" Operator.

[
TODO: Query parsing sollte in einem Duden/Http Module o.ä. sein, und nicht hier,
da die URI specification kein Query Format vorschreibt.
]

[
Ein einzelner Parameter einer Abfrage im Format "name=wert".
]
Wir nennen die öffentliche Kombination aus
dem öffentlichen Text name mit Standardwert "",
dem öffentlichen Text wert mit Standardwert "",
einen AbfragenParameter,
und erstellen sie so:
"ein AbfragenParameter <name>=<wert>",
"einem AbfragenParameter <name>=<wert>",
"ein leerer AbfragenParameter",
"einem leeren AbfragenParameter"

[
Gibt p im Format name=wert zurück.
]
Die öffentliche Funktion AbfragenParameter_als_Text mit dem Parameter p vom Typ AbfragenParameter, gibt einen Text zurück, macht:
Wenn (name von p) leer ist, gib "" zurück.
Wenn (wert von p) leer ist, gib name von p zurück.
Gib name von p verkettet mit '=' verkettet mit wert von p zurück.
Und überlädt den "als" Operator.

[
Gibt abfrage im Format "name=wert&name2=wert2..." zurück.
]
Die öffentliche Funktion AbfragenParameter_Liste_als_Text mit dem Parameter abfrage vom Typ AbfragenParameter Liste, gibt einen Text zurück, macht:
Der Text t ist "".
Für jede Zahl i von 1 bis die Länge von abfrage, mache:
Der Text p ist ((abfrage an der Stelle i) als Text).
Wenn p nicht leer ist, dann:
Füge p an t an.
Wenn i ungleich die Länge von abfrage ist, Füge '&' an t an.
Gib t zurück.
Und überlädt den "als" Operator.

[
Gibt eine Liste aller AbfragenParameter in abfrage zurück,
angenommen abfrage ist im Format "name=wert&name2=wert2..." sein (was nicht bei jeder URI der Fall ist).
]
Die öffentliche Funktion Abfragen_Parameter_Liste mit dem Parameter abfrage vom Typ Text, gibt eine AbfragenParameter Liste zurück, macht:
Wenn abfrage leer ist, gib eine leere AbfragenParameter Liste zurück.

Die AbfragenParameter Liste ergebnis ist eine Leere AbfragenParameter Liste.
Die Zahl i ist 1.
Die Zahl l ist die Länge von abfrage.
Solange i kleiner als, oder l ist, mache:
Der AbfragenParameter p ist ein leerer AbfragenParameter.

Die Zahl name_start ist i.
Erhöhe i um 1.
Solange i kleiner als, oder l ist und abfrage an der Stelle i ungleich '=' ist und abfrage an der Stelle i ungleich '&' ist, mache:
Erhöhe i um 1.

Speichere abfrage im Bereich von name_start bis (i minus 1) in name von p.

Wenn abfrage an der Stelle i gleich '&' ist, dann:
Erhöhe i um 1.
Speichere ergebnis verkettet mit p in ergebnis.
Fahre mit der Schleife fort.

Erhöhe i um 1.
Die Zahl wert_start ist i.
Solange i kleiner als, oder l ist und abfrage an der Stelle i ungleich '&' ist, mache:
Erhöhe i um 1.
Speichere abfrage im Bereich von wert_start bis (i minus 1) in wert von p.
Erhöhe i um 1.
Speichere ergebnis verkettet mit p in ergebnis.

Gib ergebnis zurück.
Und kann so benutzt werden:
"alle AbfragenParameter aus <abfrage>",
"allen AbfragenParametern aus <abfrage>"

[
Gibt den Wert des AbfragenParameters mit dem Namen name zurück
angenommen abfrage ist im Format "name=wert&name2=wert2..." sein (was nicht bei jeder URI der Fall ist).
]
Die öffentliche Funktion Abfragen_Parameter_Wert mit den Parametern abfrage und name vom Typ Text und Text, gibt einen Text zurück, macht:
Die AbfragenParameter Liste l ist alle AbfragenParameter aus abfrage.
Für jede Zahl i von 1 bis die Länge von l, mache:
Wenn (name von (l an der Stelle i)) gleich name ist, dann:
Gib (wert von (l an der Stelle i)) zurück.
Gib "" zurück.
Und kann so benutzt werden:
"den Wert des AbfragenParameters <name> aus <abfrage>"

[
Überprüft ob der Abfragen Teil einer URI einen benannten Parameter (name=) enthält,
angenommen abfrage ist im Format "name=wert&name2=wert2..." sein (was nicht bei jeder URI der Fall ist).
]
Die öffentliche Funktion Hat_Abfragen_Parameter mit den Parametern abfrage und name vom Typ Text und Text, gibt einen Wahrheitswert zurück, macht:
Die AbfragenParameter Liste l ist alle AbfragenParameter aus abfrage.
Für jede Zahl i von 1 bis die Länge von l, mache:
Wenn (name von (l an der Stelle i)) gleich name ist, gib wahr zurück.
Gib falsch zurück.
Und kann so benutzt werden:
"<abfrage> einen AbfragenParameter mit Namen <name> enthält"
64 changes: 43 additions & 21 deletions lib/stdlib/source/DDP/regex.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
#define PCRE2_CODE_UNIT_WIDTH 8
#include "DDP/ddpmemory.h"
#include "DDP/error.h"
#include "DDP/utf8/utf8.h"
#include <pcre2.h>
#include <stdio.h>
#include <string.h>


typedef struct Treffer {
ddpstring text;
ddpstringlist gruppen;
Expand Down Expand Up @@ -44,16 +44,38 @@ static pcre2_code *compile_regex(PCRE2_SPTR pattern, PCRE2_SPTR subject, ddpstri
}

static void make_Treffer(Treffer *tr, pcre2_match_data *match_data, int capture_count) {
PCRE2_UCHAR *substring;
PCRE2_SIZE substring_length;
int num_capture_groups = pcre2_get_ovector_count(match_data);

ddp_ddpstringlist_from_constants(&tr->gruppen, capture_count - 1);
for (int i = 0; i < capture_count; i++) {
pcre2_substring_get_bynumber(match_data, i, &substring, &substring_length);
ddp_ddpstringlist_from_constants(&tr->gruppen, num_capture_groups - 1);
for (int i = 0; i < num_capture_groups; i++) {
ddpstring *dest = i == 0 ? &tr->text : &tr->gruppen.arr[i - 1];

ddp_string_from_constant(i == 0 ? &tr->text : &tr->gruppen.arr[i - 1], (char *)substring);
PCRE2_SIZE substr_len;
int rc;
switch ((rc = pcre2_substring_length_bynumber(match_data, i, &substr_len))) {
case 0: // success
break;
case PCRE2_ERROR_UNSET:
*dest = DDP_EMPTY_STRING;
continue;
case PCRE2_ERROR_NOSUBSTRING:
case PCRE2_ERROR_UNAVAILABLE:
ddp_error("Keine Gruppe mit Nummer %d vorhanden", false, i);
continue;
default:
ddp_error("Die Länge von Gruppe %d konnte nicht bestimmt werden: %d", false, i, rc);
continue;
}

pcre2_substring_free(substring);
dest->cap = substr_len + 1;
dest->str = DDP_ALLOCATE(char, dest->cap);
substr_len = dest->cap;

switch (pcre2_substring_copy_bynumber(match_data, i, (PCRE2_UCHAR8 *)dest->str, &substr_len)) {
case PCRE2_ERROR_NOMEMORY:
ddp_runtime_error(1, "out of memory during regex parsing: %lld", substr_len);
continue;
}
}
}

Expand Down Expand Up @@ -81,13 +103,13 @@ void Regex_Erster_Treffer(Treffer *ret, ddpstring *muster, ddpstring *text) {

// Perform the match
int rc = pcre2_match(
re, // the compiled pattern
subject, // the subject string
text->cap, // the length of the subject
0, // start at offset 0 in the subject
0, // default options
match_data, // block for storing the result
NULL // use default match context
re, // the compiled pattern
subject, // the subject string
utf8_strlen(text->str), // the length of the subject
0, // start at offset 0 in the subject
0, // default options
match_data, // block for storing the result
NULL // use default match context
);

// Check the result
Expand Down Expand Up @@ -135,9 +157,9 @@ void Regex_N_Treffer(TrefferList *ret, ddpstring *muster, ddpstring *text, ddpin
// Perform the match
while (i < n || n == -1) {
int rc = pcre2_match(
re, // the compiled pattern
subject, // the subject string
text->cap, // the length of the subject
re, // the compiled pattern
subject, // the subject string
utf8_strlen(text->str), // the length of the subject
start_offset,
0, // default options
match_data, // block for storing the result
Expand Down Expand Up @@ -265,9 +287,9 @@ void Regex_Spalten(ddpstringlist *ret, ddpstring *muster, ddpstring *text) {
// Perform the match
while (true) {
int rc = pcre2_match(
re, // the compiled pattern
subject, // the subject string
text->cap, // the length of the subject
re, // the compiled pattern
subject, // the subject string
utf8_strlen(text->str), // the length of the subject
start_offset,
0, // default options
match_data, // block for storing the result
Expand Down
57 changes: 57 additions & 0 deletions tests/testdata/stdlib/Uri/Uri.ddp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
Binde "Duden/Ausgabe" ein.
Binde "Duden/Uri" ein.
Binde "Duden/Fehlerbehandlung" ein.

Schreibe ab jetzt alle Fehler.

Die Funktion Schreibe_Uri_Komponente mit dem Parameter uri vom Typ Uri, gibt nichts zurück, macht:
Schreibe (uri als Text verkettet mit " | ").
Schreibe (Schema von uri verkettet mit " | ").
Schreibe (Nutzer von uri verkettet mit " | ").
Schreibe (Host von uri verkettet mit " | ").
Schreibe (Port von uri verkettet mit " | ").
Schreibe (Pfad von uri verkettet mit " | ").
Schreibe (Abfrage von uri verkettet mit " | ").
Schreibe (Fragment von uri) auf eine Zeile.
Und kann so benutzt werden:
"Schreibe <uri>"

Schreibe ("" als Uri).
Schreibe ("https://user:[email protected]:8080/index.html?a=hi&b=bye#anchor" als Uri).
Schreibe ("https://de.wikipedia.org/wiki/Uniform_Resource_Identifier" als Uri).
Schreibe ("https://ddp.bafto.dev/Spielplatz/?nolines&readonly" als Uri).
Schreibe ("ftp://ftp.is.co.za/rfc/rfc1808.txt" als Uri).
Schreibe ("file:///C:/Users/Benutzer/Desktop/Uniform%20Resource%20Identifier.html" als Uri).
Schreibe ("file:///etc/fstab" als Uri).
Schreibe ("geo:48.33,14.122;u=22.5" als Uri).
Schreibe ("ldap://[2001:db8::7]/c=GB?objectClass?one" als Uri).
Schreibe ("gopher://gopher.floodgap.com" als Uri).
Schreibe ("mailto:[email protected]" als Uri).
Schreibe ("mailto:[email protected],[email protected]" als Uri).
Schreibe ("mailto:[email protected]?subject=Betreff&[email protected]&body=Dies%20ist%20eine%20Textnachricht" als Uri).
Schreibe ("sip:[email protected]" als Uri).
Schreibe ("news:comp.infosystems.www.servers.unix" als Uri).
Schreibe ("data:text/plain;charset=iso-8859-7,%be%fa%be" als Uri).
Schreibe ("tel:+1-816-555-1212" als Uri).
Schreibe ("telnet://192.0.2.16:80/" als Uri).
Schreibe ("urn:oasis:names:specification:docbook:dtd:xml:4.1.2" als Uri).
Schreibe ("git://github.com/rails/rails.git" als Uri).
Schreibe ("crid://broadcaster.com/movies/BestActionMovieEver" als Uri).

Schreibe "\nAbfragen\n" auf eine Zeile.

Schreibe ("a=b&c=d" einen AbfragenParameter mit Namen "a" enthält) auf eine Zeile.
Schreibe ("a=hi&b=bye" einen AbfragenParameter mit Namen "b" enthält) auf eine Zeile.
Schreibe ("a=b&c=d" einen AbfragenParameter mit Namen "d" enthält) auf eine Zeile.
Schreibe ("a&c=d" einen AbfragenParameter mit Namen "a" enthält) auf eine Zeile.
Schreibe ("" einen AbfragenParameter mit Namen "a" enthält) auf eine Zeile.

Schreibe ((alle AbfragenParameter aus "a=b&c=d") als Text) auf eine Zeile.
Schreibe ((alle AbfragenParameter aus "a&c=d") als Text) auf eine Zeile.
Schreibe ((alle AbfragenParameter aus "a=hi&b=bye") als Text) auf eine Zeile.
Schreibe ((alle AbfragenParameter aus "") als Text) auf eine Zeile.

Schreibe (den Wert des AbfragenParameters "a" aus "a=b&c=d") auf eine Zeile.
Schreibe (den Wert des AbfragenParameters "a" aus "a&c=d") auf eine Zeile.
Schreibe (den Wert des AbfragenParameters "a" aus "a=hi&c=d") auf eine Zeile.
Schreibe (den Wert des AbfragenParameters "a" aus "") auf eine Zeile.
Loading
Loading