Vollständige Version anzeigen : PHS: Meine Wunsch-Weiterentwicklung von PHP (Programmiersprache)


Murdoc
01.11.2014, 17:58

Um was gehts?

Wie manche vielleicht wissen arbeite ich seit geraumer Zeit an einer eigenen Programmiersprache die - zumindest für mich - PHP in vielen Dingen ablösen bzw. erleichtern soll.

Ich habe die Sprache PHS genannt, weil ich der Meinung bin, dass PHP stinkt (PHP Sucks). Aber nicht weil PHP zu wenig kann oder die Syntax doof wäre, nein, wenn das der Fall gewesen wäre hätte ich meine Sprache direkt für die JVM oder HHVM konzipiert. Der Grund warum PHP stinkt ist (für mich) das alter der Sprache.

Sicherlich werden immer mehr tolle Features in PHP implementiert (z;b. anonyme Funktionen oder vor Kurzem Variadic-Argumente), die die Sprache moderner erscheinen lassen, aber viele Dinge, die man erwarten würde können nicht mehr nachgereicht werden, weil diese nicht abwärtskompatibel wären.

Daher meine Idee: Warum nicht einfach einen Compiler entwerfen, der diese Features alle nachrüstet?

Für die Syntax und Semantik der Sprache habe ich mich von vielen anderen Sprachen inspirieren lassen.
Namhafte Beispiele wären:


Rust
JavaScript
Java
Dart
C++ und D
Python


Folgende Dinge hatte ich auf meinem Zettel:


Module statt Namensräume
Private Funktionen, Variablen und Klassen (..;) in Dateien (wie es mittels "static" auch in C/C++ möglich ist)
Block-Geltungsbereiche *
Vom Compiler konstruiertes capturing von Variablen in Funktionen -> function() use($bar) { $bar; }
Funktionale Aspekte (Funktionen als Objekt statt als String übergeben können, Objekt-Eigenschaften ausführbar machen)
Festgelegte Klassenhierarchie (Standard Elternklasse wird implizit vererbt)
Objekte statt Assoziative Arrays (Assoziatives-Array-vs-Objekt-Benchmark)
Fokus auf die aktuelle Entwicklung von PHP statt Altlasten nachzubauen


* Block-Geltungsbereiche lassen sich in etwa so umschreiben:
;;1;xup~to/tn/2014_11/17319702;png (;;;xup~to/dl,17319702/block-scope-oprah;png/)


Wie soll das funktionieren?

Darüber habe ich wirklich lange Zeit nachgedacht. PHP kann alles was ich will, aber zwingt mir teilweise eine komische handhabe auf die man im Grunde gar nicht wirklich braucht wenn man den Code ein wenig inspiziert. Dieses verhalten wurde zwar mittlerweile geändert (;wiki;php~net/rfc/abstract_syntax_tree), aber das bringt nicht viel, da, wie bereits angesprochen, der Code immer noch abwärtskompatibel bleiben muss.

Mein Compiler geht da ein paar Schritte weiter. Nach dem verarbeiten des Quelltextes wird ein Syntaxbaum erstellt, welcher anschließend in mehreren (teils recht aufwendigen) Schritten untersucht wird. Damit ist es möglich noch vor dem Ausführen des Codes viele wichtige Informationen zu sammeln, mit dessen Hilfe im nächsten Schritt optimierter und vor allem sinnvoller Code generiert werden kann.

Folgendes wird (u;A;) bereits vor dem Ausführen bestimmt:


Kann auf eine Variable oder Funktion/Klasse ... zugegriffen werden? (wenn es 100% ersichtlich ist)
Welche Symbole werden in einer Funktion referenziert? (Welche Variablen müssen `eingefangen` werden)
Was wird mit den Symbolen gemacht (wird eine Funktion aufgerufen oder nur abgefragt?)
Macht parent::xyz() überhaupt Sinn? (existiert eine Elternklasse* und hat diese Zugriff auf eine Methode namens "xyz"?)
Wird eine Variable noch gebraucht?


* Wie bereits erwähnt erben alle Klassen nun von einer Standardelternklasse, aber dazu später mehr


Die Syntax

Reservierte Wörter:

fn let use enum class trait iface module require
true false null this super self get set
do if elif else for try goto break continue throw
catch finally while assert switch case default
return print const final static extern
public private protected
__sealed__ __inline__ __global__ __php__
__test__ __end__
yield new del as is in
int integer bool boolean float double string
__dir__ __file__ __line__ __coln__
__fn__ __class__ __trait__ __method__ __module__


Das sind auf den ersten Blick ggf. zu viele, aber daran arbeite ich noch.


Funktionen deklarieren

fn func() {
// weiterer code
}



Variablen deklarieren

let var = 1234;


Klassen/Traits/Interfaces

iface Foo {
fn method();
}

trait Bar {
fn trait_method() {
// weiterer code
}
}

class Baz {
fn func() {
// weiterer code
}
}

class Qrz: Baz ~ Foo {
use Bar;

let prop = 1234;

new () {
// constructor
}

del () {
// destructor
}

fn method() {
// weiterer code
}
}



Weitere Beispiele folgen!


Ein Problem mit Variablen

Variablen in PHP werden so gut wie immer implizit erstellt (d;h. müssen nicht deklariert werden). Zudem kennt PHP nur zwei (ausgenommen Klassen/Objekt Kontext) Geltungsbereiche für Variablen: Global oder innerhalb einer Funktion.

Dieses Verhalten war mir zu einfach gestrickt und zu unflexibel. Aus diesem Grund habe ich mich von Java/C/C++ und vielen anderen Programmiersprachen inspirieren lassen und Block-Geltungsbereiche implementiert, die es möglich machen Variablen nur für einen bestimmten Bereich im Speicher zu halten (fernab von Funktionen). Zudem müssen in meiner Sprache Variablen nun explizit deklariert werden, damit der jeweilige Geltungsbereich einwandfrei ersichtlich ist.

Beispiel:

{
// beginn: Block-Geltungsbereich
let foo = 1;
print foo;
} // ende: Block-Geltungsbereich, `foo` wird an dieser Stelle gelöscht



Funktionen

Ein weiteres komisches Verhalten von PHP bekommt man zu spüren wenn man mit Funktionen arbeitet.
PHP kennt zwei Arten von Funktionen: Normale Funktionen und Lambdas (aka. Anonyme Funktionen aka. Closures).

Lediglich Lambdas eignen sich ohne Workaround zu funktionalen Programmierung. Dabei muss man neidlos anerkennen, dass die Implementation von Closures (Lambdas innerhalb von Methoden) gut durchdacht wurde und sich ohne weiteres zu tun sinnvoll verwenden lässt.

Normale Funktionen hingegen lassen sich im Grunde nur aufrufen. Anderer Zugriff ist nur mittels Strings (der Name der Funktion als String) möglich.
Zudem ist es nicht möglich Funktionen innerhalb von anderen Funktionen zu deklarieren, bzw. es ist möglich, aber die Funktion wird dann global deklariert, was nur einmalig funktioniert und das Ganze ad absurdum führt.

Daher habe ich das Konzept von Funktionen komplett überdacht und, wie ich finde, eine gute Lösung gefunden:


Deklariert man eine globale Funktion erhält man eine normale Funktion (wie in PHP auch)
Deklariert man eine Funktion in einem Block (also auch innerhalb einer anderen Funktion) erhält man ein Lambda
Referenziert man eine normale Funktion lesend (z;b. als Argument für eine andere Funktion) erhält man automatisch dessen absoluten Namen als String *
Bei der Deklaration von Funktionen werden automatisch Referenzen innerhalb der Funktion aus dem gleichen oder übergeordneten Geltungsbereich erfasst (wie z;b. in JavaScript) - Der Compiler weiß an dieser Stelle was mit "use" und was mit "global" referenziert werden muss/kann/soll ;)
Alle Funktionsdeklarationen sind automatisch konstant, egal ob der Compiler ein Lambda generiert hat oder nicht.


* Selbes Prinzip trifft auch auf Klassen, Traits und Interfaces zu, aber dazu später mehr

Ein weiteres Problem sind Closures als Eigenschaften von Objekten.
PHP unterschiedet hier strikt zwischen Eigenschaften und Methoden und erlaubt lediglich den Zugriff auf Methoden wenn man die $obj->prop() Syntax verwendet. Ob hier ggf. eine Eigenschaft namens "prop" existiert ist dem PHP-Interpreter schlicht egal.

Dieses Problem lässt sich mit einem Workaround lösen:

$fn = $obj->prop;
$fn();


Das wäre sicherlich auch möglich gewesen, aber sehr sehr schwer zu implementieren in einer dynamischen Sprache (welchen Typ hat $obj? Ist "prop" eine Methode?).
Daher habe ich mich dazu entschlossen eine Standard-Elternklasse einzubauen, wie man sie auch von Java und JavaScript (und neuem Python) kennt. Diese Standard Klasse implementiert eine __call Methode genau für diesen Zweck.

function __call($prop, $args) {
$func = $this->{$prop};
return $func(..;$args);
}


Dem einen oder anderen fällt vielleicht auf, dass so keine Referenzen mehr übergeben werden können. Das stimmt und steht bereits auf meiner Todo-Liste. Dazu sei gesagt, dass der PHP-Workaround ohne Probleme auch in meiner Sprache funktioniert falls das ein Problem darstellen sollte.


Namensräume

Namensräume existieren nur im internen Code des Compilers in den Symboltabellen.

Folgende Tabellen werden hierbei genutzt:

Module
Klassen, Traits und Interfaces
Funktionen und Variablen


Jep, Funktionen und Variablen teilen sich den gleichen Namensraum.

Diese Entscheidung lag auf der Hand als ich mit der Konzeption von Funktionen fertig war. Wenn der Compiler je nach Kontext eine normale Funktion oder ein Lambda generiert, macht es nicht viel Sinn diese beiden Symboltypen voneinander zu trennen - wie es PHP tut.

Das hat zudem einen riesen Vorteil innerhalb von Klassen, denn wie bei normalen Funktionen kann PHP auch Methoden nicht lesend ansprechen, sondern nur aufrufen, es sei denn man nutzt auch hier einen Workaround:

map($obj->func); // klappt nicht
map([ $this, 'func' ]); // klappt


Daher dachte ich mir: Wenn Funktionen und Variablen in meinem Compiler im selben Namensraum definiert werden, warum nicht einfach folgendes von Haus aus implementieren:

class Foo {
public $func;

public function __construct() {
// implizit
$this->func = [ $this, 'func' ];
}

public function func() {
echo "hello world\n";
}
}

$obj = new Foo;
$obj->func(); // methodenaufruf
$func = $obj->func; // zugriff auf eigenschaft "func"
$func(); // selbes ergebnis wie $obj->func();



Module

Als Ersatz zu PHP "namespace" habe ich mir ein richtiges Modulsystem ausgedacht. Aber anstatt hier, wie in PHP, stumpf nur Klassen, Funktionen, Traits, Interfaces und "echten" Konstanten ein "Präfix" zu verpassen dachte ich einen Schritt weiter.

Was ist mit Variablen? Und warum kann ich nicht angeben auf welche Symbole von außen (wie bei Klassen) zugegriffen werden darf?
Module werden sowieso rein vom Compiler verwaltet, also warum bohren wir das Ganze nicht ein wenig auf!

Daraus entstand ein Modulsystem das dem von JavaScript und Rust ähnelt:


Module können alle möglichen Symbole verwalten (inklusive Variablen)
Es ist möglich nur bestimmte Symbol zu "exportieren" (wie in Klassen)
Mittels "use" kann man alle möglichen Symbole referenzieren (auch Variablen)
Mittels "use" lassen sich Aliase definieren (ähnlich wie "export")


Im Grunde kann man sich ein Modul wie eine große statische Klasse vorstellen.
Zugriff auf Symbole erfolgt absolut (man spricht hier von "voll qualifiziert) oder mittels "use" (wie man es von PHP bereits kennt).

module foo {
let bar = 1; // privat
public baz = 2; // öffentlich

public use self::baz as qrz; // öffentliches alias ("self" bezieht sich auf das modul)
public use anderes_modul; // öffentliches alias auf ein anderes modul
use noch_ein_anderes_modul; // privates alias auf ein anderes modul
}

foo::bar; // zugriff auf privates symbol -> fehlermeldung
foo::baz; // okay
foo::qrz; // entspricht foo::baz




Das war es soweit, weiteres wird nach und nach ergänzt!

Mich würde brennend interessieren was ihr davon haltet!
Eine ausführliche Dokumentation wird erstellt wenn mein Compiler benutzbar ist (das Gröbste ist aber bereits implementiert!).

Den Code kann man sich soweit bei Github ansehen:
droptable/phs-lang · GitHub (;github~com/droptable/phs-lang) (Lines of Code derzeit: ~25;000)

Eine erste Alpha-Version sollte in den nächsten Wochen folgen!

Hardware Preisvergleich | Amazon Blitzangebote!

Videos zum Thema
Video Loading...
Murdoc
23.11.2014, 15:29

Erste Alphaversion:

Download: phsc;zip | ;;xup~to (;;;xup~to/dl,16688551/phsc;zip/)

Im Archiv befindet sich in phsc/bin eine Konsole für Windows (cmd-XX;bat) und die jeweiligen Shell-Skripte:

phsc;bat für Windows
phsc~sh für *nix

Achtung: Der Compiler läuft mit PHP 5;5+, die erzeugten Skripte benötigen PHP 5;6+

Zuerst könnt ihr testen ob der Compiler überhaupt läuft:

phsc -?

Im Idealfall sollte eine Liste mit möglichen Optionen für den Compiler ausgegeben werden.
Sollte das nicht funktionieren, dann müsst ihr zusätzlich noch PHP installieren (ich nutze /usr/bin/env um die PHP-Binary aufzulösen).

Oder falls ihr mit Windows unterwegs seid:

Falls kein XAMPP installiert ist oder was ähnliches:
PHP For Windows: Binaries and sources Releases (;windows;php~net/download/)

(TS oder NTS spielt keine Rolle).

Die ;zip runterladen und entpacken wo sie euch nicht weiter stört, aber nicht vergessen wo, denn:

Den Pfad zur php;exe in die PATH Umgebungsvariable ausfnehmen:

Klick auf [Start]
Suche nach "System" bis es unter "Systemsteuerung" auftaucht und klick den Eintrag an
"Erweiterte Einstellungen" in der Sidebar wählen
Den Button "Umgebungsvariablen" klicken
Falls unter "Benutzervariablen" kein Eintrag namens "Path" existiert (oder PATH) -> diesen anlegen
Den Eintrag "Path" (oder auch PATH) bearbeiten
Ganz an das Ende der Texteingabe ein Semikolon setzen (;) und dann den absoluten Pfad zur PHP;exe einfügen (ohne "php;exe" am Ende!)
Das Fenster mit "Okay" schließen
fertig



Die Konsole schließen und neu öffnen!

Sollte das Ganze immer noch nicht funktionieren lasst es mich wissen.

Ein installer ist in Planung.


Hello World

Legt eine Datei namens "test;phs" (der Einfachheit halber in phsc/bin) an mit Inhalt:

print "hello world";

Dann folgendes ausführen:

phsc -r -o test;phar test;phs

Idealerweise sollte nun "hello world" in der Konsole ausgegeben werden :)

Info:

Die Option "-r" ist dazu da die übersetze Datei gleich auszuführen.
Der Start der Datei dauert ein wenig, aber das ist normal.

Um die Datei nur zu übersetzen einfach die "-r" Option weglassen und die erzeugte Datei selbst ausführen (ganz normal mit PHP), dann geht das ein wenig flotter.

phsc -o test;phar test;phs
php test;phar
Einmal übersetzt ist die Datei ganz normal wie andere PHP-Skripte ausführbar (muss nicht erneut übersetzt werden).

Webserver

Wer das Ganze lieber von seinem Apachen ausgeliefert haben will muss das Skipt ein wenig anders übersetzen:

phsc -o test;phar --pack phar-web test;phs

Die Option "--pack phar-web" erzeugt eine Webfähige PHAR-Datei:
PHP: Phar::webPhar - Manual (;php~net/manual/de/phar;webphar;php)

Anschließend die erzeugte Datei "test;phar" in den htdocs-Ordner kopieren und ganz normal aufrufen.
Falls es nicht auf Anhieb funktioniert muss ggf. noch "phar" als PHP-Datei registriert werden, das geht so:

<FilesMatch "\;phar$">
SetHandler application/x-httpd-php
</FilesMatch>


Es stehen noch weitere Modi zur Verfügung, z;b. "--pack zip" oder "--pack none", Infos dazu gibts mit dem Kommando:
phsc -?


Ähnliche Themen zu PHS: Meine Wunsch-Weiterentwicklung von PHP (Programmiersprache)
  • Umfrage über die Weiterentwicklung der digitalen Filmangebote
    Nabend zusammen, ein freund von mir bräuchte einige ausgefüllte umfragen. Von euch werden natürlich keinerlei Daten gebraucht, ist klar ;) Die Umfrage ist mit 7 Schritten auch relativ kurz gehalten. Ich würde mich freuen wenn Ihr die Umfrage weitersenden könntet um dort einige Ausgefüllte bo [...]

  • [Layout] kleiner filehoster 2 (weiterentwicklung)
    Hallo, vielleicht können sich einige noch an meinen alten Thread erinnern. In dem wurde von meinem Design mehr Struktur gefordert. Nun habe ich mich drangesetzt und weiter gearbeitet. Bin eigentlicht recht zufrieden. Kritik erwünscht. link entfernt [...]

  • Verhindert die Demokratie die Weiterentwicklung?
    Hey, ich hab mir mal Gedanken gemacht gehabt über unsere Demokratie und ihrer Geschichte und alles drum rum. Und wie ihr seht bin ich zum Schluss gekommen, dass die Demokratie selbst die Weiterentwicklung eines Staatssystem doch eigentlich selbst blockiert. Angenommen es gäbe ein besseres Staat [...]

  • Ubisoft - Mögliche DRM-Weiterentwicklung
    Ubisoft wird den bereits im Einsatz befindlichen und heiß diskutierten DRM-Kopierschutz weiter verbessern (wir berichteten (;;;4players~de/4players;php/spielinfonews/360/421/2009213/Ubisoft;html)). Im Slashdot-Forum (;games;slashdot~org/comments;pl?sid=1630600&cid=31975686) hat sich nun ein anonym [...]



raid-rush.ws | Imprint & Contact pr