Language Design: HTML Templates



  • Hallo Leute!

    Ich werke gerade an einer Engine für HTML Templates rum - so etwas ähnliches wie Smarty - nur eben in C++.

    Allerdings weiss ich nicht so genau, wie ich diese 'Sprache' designen soll. Ich hoffe ihr könnt mir dabei helfen 🙂

    Die Tags stehen zwischen zwei Delimitern (zB { und } - kann der User sich aber aussuchen.

    Prinzipiell möchte ich alle Sprachkonstrukte wie if, foreach,... nicht in die Sprache integrieren, sondern in die Library, so dass sich jeder sein eigenes if schreiben kann - wenn ihm das Standard-if nicht reicht.

    Doch erstmal zu den Tags:
    {foo}
    Kann entweder eine Funktion oder eine Variable sein. Zuerst wird gecheckt ob es keine Variable ist - denn die haben Vorrang.
    {toupper what="hallo"}
    würde durch HALLO ersetzt werden.
    Die Parameter sollen also Namen bekommen. Die Sprache soll nämlich für Webdesigner sein - und nicht für die Programmierer.

    Problem ist jetzt aber:
    {toupper what=foo}
    funktioniert auch. die Variable foo wird mit Grossbuchstaben ausgegeben.
    Doch wie realisiere ich einen Funktionsaufruf?
    {toupper what={tolower what=foo}}
    Das erscheint mir irgendwie unlogisch. Somit müsste man Variablen auch immer mit {} einschliessen (da ich Funktionen und Variablen nicht trennen will - eine Variable ist eine Funktion ohne Parameter)
    {toupper what={tolower what={foo}}}
    Da gefällt mir die lesbarkeit leider nicht...

    Zu dem if, foreach, etc.
    {if cond={foo}
    {if cond={isupper what={foo}}}
    würde jeweils bis zum nächsten
    {/if}
    alles einlesen und an if übergeben.

    Ob die Funktion Normal ist (wie zB toupper) oder Section (wie zB if) ist, sagt die Funktion dem Parser. Für den User sehen sie identisch aus, lediglich beim registrieren der Funktion beim Parser wird angegeben was für eine Funktion es ist.

    Ein Problem habe ich noch mit Arrays. Wie sollte ich die vernünftig handhaben?
    Eine Funktion kann entweder einen normalen Wert zurückgeben, ein Array oder garnix. Wenn die Funktion nichts zurück gibt - ist es einfach ein leerer string - aber wie mache ich es bei Arrays?

    Was haltet ihr davon: ein Datentyp der immer ein Array ist - nur dass ein string eben nur 1 Element hat.

    Wenn wir jetzt zB eine Liste aller Besucher an das Template übergeben, könnte man diese so darstellen:
    {foreach in={users} as="user"}
    {user}
    {/foreach}
    doch was macht man, wenn user selbst ebenfalls ein Array ist, mit zB Vorname und Nachname?

    {foreach in={users} as="user"}
    {user get="Vorname"}
    {user get="Nachname"}
    {/foreach}

    Doch wie sollte man das implementieren? user müsste dazu ja eine map sein - doch eine map wäre wiederum zu aufwendig für eine simple liste/aufzählung ala "Apfel", "Banane", "Birne".

    Ich dachte daran, dass man irgendwo eine Mapping-Tabelle anlegen könnte. {user get="Vorname"} schaut sich dann die Tabelle für die ID 'user' an (die man natürlich vorher einmal anlegen muss) und "Vorname" wird dann zum passenden index gemappt. Jede Variable muss dann lediglich die eigene Mapping ID kennen.

    Nur irgendwie wirkt dies etwas zu komplex auf mich - das müsste auch einfacher gehen, oder?

    Und wie sieht es mit den verschiedenen Typen aus?
    {if cond={equal a=1.2 b="1.2"}}
    dies solte true liefern...

    Momentan wird alles in strings umgewandelt - problem sind die Kosten bei einem
    {round what=1.234 prec=2}
    Hier wird 1.234 und 2 erst in einen string umgewandelt, dann an round übergeben, dort wieder zu einem float umgewandelt, gerundet und dann wieder zu einem string umgewandelt.
    Könnte man dies irgendwie effizienter gestalten? Etwa ein Variant-Datentyp ala boost::any?

    Danke für eure Ideen! 👍 👍



  • Hallo,
    Ich bin mir nicht sicher, ob ich jetzt alles richtig verstanden habe, aber ein paar eher allgemeine Ideen habe ich:

    Zunächst mal oberstes Prinzip: Versuche so viele Dinge wie möglich zusammen zu behandeln. Zum Beispiel Normale Variablen und Arrays. Wie wär's, wenn Du eine Schnittstelle hast, die beides kann. Wenn jetzt jemand auf eine normale Variabla wie bei einem Array zugreift, dann kriegt er von der entsprechend überschriebenen Methode eins auf die Finger.
    In sich verwaltet ein Array nur Zeiger auf weitere dieser Objekt. Damit können die entweder wieder arrays oder normale Objekte sein. Usw.

    Damit sollten zumindest die Probleme mit dem for_each, was wenn dieses Teil wieder ein Array ist erledigt sein.

    Vielleicht hilft Dir das ein bißchen... oder auch nicht. 😉

    MfG Jester



  • Jester schrieb:

    Zum Beispiel Normale Variablen und Arrays. Wie wär's, wenn Du eine Schnittstelle hast, die beides kann. Wenn jetzt jemand auf eine normale Variabla wie bei einem Array zugreift, dann kriegt er von der entsprechend überschriebenen Methode eins auf die Finger.

    Du meinst also eine abstrakte Klasse 'Datentyp' von der string, int, array und map erben? Wobei array und map aus n 'Datentyp'en besteht?

    Der Zugriff würde dann über
    variable->get("Vorname");
    ablaufen - und uU eben eine std::runtime_error werfen, wenn das Ding keine map ist?

    Irgendwie gefällt mir das noch nicht so ganz, da Datentyp somit get() als Member haben muss, obwohl die meisten Subklassen diese nicht brauchen...

    Aber thx für die Idee - da kann man vielleicht was draus machen.
    👍 👍



  • Ich habs mir beim essen nochmal durch den Kopf gehen lassen.

    Die Idee ist doch sehr gut.

    Einziges Problem:

    Ich will einer int Variablen einen 'string' zuweisen (zB "1.34") - wie sollte ich das realisieren? Einfach ein festdefiniertes set an variablentypen mit konvertierung untereinander? Oder einfach int weglassen und wie gehabt alles als string speichern?



  • Da sehe ich mehrere Möglichkeiten:

    Entweder alles über string. Wenn die Performance okay ist, kann man das ja lassen. Oder aber Typen einschränken und alle Konvertierungen zwischen diesen festlegen. Das finde ich aber nicht so toll, weil man für n Typen n^2 verschiedene Konvertierungen einbauen muß. Also wird das einfügen von neuen Typen immer arbeitsintensiver.

    Wir wär's, wenn jeder Datentyp von und nach string konvertierbar ist. Wenn zwei Variablen ineinander konvertiert werden sollen, dann geschieht das über string, es sei denn, es ist eine explizite Konvertierung angegeben, dann wird diese verwendet. Dadurch kannst Du häufig benutzte oder einfach Konvertierungen selbstschreiben und standardmäßig wird der Umweg über string genutzt.

    MfG Jester


Anmelden zum Antworten