Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

1

01.06.2014, 20:33

[Snippet] Eigene Scriptsprache

[Edit] Da der Thread viele Aufrufe hat und bei Google auch zügig gefunden wird, verlinke ich hier oben noch mal das GitHub Repo, da dieses wohl ist was viele suchen werden (Code).

So mich hat das etwas frustriert mit dem Shader Kram, daher mache ich gerade mal was anderes ;)

Ich versuche eine eigene Sprache zu entwickeln! Das klappt auch soweit eigentlich ganz gut.
Wenn das ganze was wird baue ich es in meine Engine ein, wenn nicht nehme ich was fertiges. Ich finds aber echt ganz interessant.

Hier mal ein Beispiel:

Quellcode

1
2
3
4
5
6
7
8
# single line comment

_a = 5 # int
_b = 3 # noch ein int

if(_a > _b){
    print "Hallo Welt!" # Konsolenausgabe
}


Also recht einfach. Folgendes funktioniert schon:

1. Kommentare (beginnend mit #) werden rausgeschmissen (außer in strings natürlich).
2. Der Code wird in Token zerlegt, also z.B. _a = 5 in "_", "a", "=" und "5".
3. Die Token werden in einen Syntax Tree geparsed.
4. Der Syntax Tree wird binär in eine Datei geschrieben (mit Werten und Variablennamen).
5. Der Code kann eingelesen und "ausgeführt" werden.

Aktuell ist natürlich nicht alles drin. Ich versuche erstmal Variablen einzulesen und auszugeben:

Quellcode

1
2
_a = 5
_b = 3


Wenn ich den Syntax Tree ausgebe (rekursirv):

Quellcode

1
2
3
4
5
6
7
8
_
a
=
5
_
b
=
3


Wenn ich die VM laufen lasse (und dabei Testweise in die Konsole schreibe):

Quellcode

1
2
3
4
5
6
7
8
Variable
a
Assignment
5
Variable
b
Assignment
3


Also soweit schon mal recht cool.

Was ich mich jetzt frage: wie speichere ich dynamisch (!) Variablen? Also später auch Objekte die dynamisch angelegt werden können?
Gehört habe ich von der Möglichkeit eines Stacks, also wenn Variablen temporär genutzt werden push() und pop(), für die anderen wird der Name (string) aus dem Code in einen Index umgewandelt.
Java macht aus dem Namen glaube ich einen Hash Wert, der dann als eine Art Index dient.

Also, was würdet ihr nehmen/bietet sich da an?

Den ganze Code gibts hier.

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »DeKugelschieber« (22.06.2014, 22:10)


BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

2

01.06.2014, 20:37

Map<String,SpecialVarType>
Bis die Performance was anderes verlangt.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

3

01.06.2014, 21:30

Naja das hat noch den Nachteil dass ich ja bestimmen muss, wann ein String anfäng (also auch Variablennamen), und wann er endet.
Das heißt es wäre sicherlich einfacher vorher alle Variablen durch eine ID zu ersetzen, dann hab ich eine feste größe die ich einlesen kann (z.B. 4 Byte). Und kann für scopes eine Liste mit Stack verwenden...
So stelle ich mir das vor:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
_a = 5
print _a

# wird zu:

Variable
ID 1
Assignment
5
Print
Variable
ID 1


Das werde ich auch erst so versuchen.
Für die Objekte ist das nur problematisch, vermute ich...

Wie machen das denn Python, Java, C# und Co?

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

4

01.06.2014, 22:06

Wenn Du sie durch IDs ersetzen kannst, mach das. Sprachen mit JIT-Compilern dürften da nicht relevant sein ;) Wie die JVM oder Python das machen, weiß ich nicht.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

5

04.06.2014, 10:41

So ja funktioniert ganz gut mit Indices, aber mein Parser ist noch nicht in der Lage für die VM die Tokens in eine lineare Struktur zu bringen.
Das Problem:

Quellcode

1
2
_a = 5
print _a # _a ist kein Befehl, woher weiß print also nun, dass das danach ausgegeben werden soll?


Der Parser liest _, macht daraus ein Variable Token, dann a, ein VariableName Token, dann = Assign Token, 5 Value Token.
Wobei das "oben" an das erste Token nach und nach angehangen wird.
So, das lässt sich so linear durch die VM jagen, aber das print müsste ich eigentlich anders parsen, erst _, a dann print.

Das wird noch häufiger kommen, z.B. (das +):

Quellcode

1
2
3
4
5
6
  _         | Variable Token
 / \
a   +       | VariableName Token, Add Token
   / \
  /   \
 4     5    | Value Tokens


Wie parse ich solche Fälle am besten?

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

6

04.06.2014, 10:49

Du trennst aber schon Lexer und Parser, ja?
Und wieso wird _a als zwei Token geparst? Es sollte nur eines sein (da es ja logisch zusammenhängt): Tok.Identifier oder sowas.

Zum Zeitpunkt des Lexen weiß print überhaupt nicht, was _a ist. Das geschieht erst im Parse Vorgang. Wir erkennen print als Ausgabe Befehl, sehen, dass danach ein Identifier folgt, identifizieren, was dieser Identifier ist (also z.B. eine Variable) und lösen auf. Eig. geschieht dieser Auflöse Prozess in der semantischen Phase.
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

7

05.06.2014, 09:30

Ja Lexer und Parser sind getrennt, ist vielleicht etwas verwirrend weil ich ständig Token gesagt habe.
Aber meine Token (liegen als strings vor) parse ich dann ja in einen Syntax Tree aus Objekten spezieller Klassen.

Ich bin also tatsächlich schon beim parsen.
Die Token kommen allerdings linear, so wie sie gelesen wurden. Für die Baumstruktur müsste ich aber häufig auch Token vorher lesen, so wie das + oben.
Oder zumindest den Tree dann so umbauen, dass das Plus vorne steht. Je nachdem wie mans macht.

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

8

05.06.2014, 09:51

Polnische Notation wäre vllt. etwas interessantes für dich.
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

9

06.06.2014, 12:19

Hmm meinst du die Syntax anzupassen?
Fände ich doof.

Ich habs jetzt soweit dass der Syntax Tree auch "Rückwärts" erstellt werden kann. Also sowas:

Quellcode

1
_a = 1


Wird zu:

Quellcode

1
2
3
  =     | Assignment
 / \
_   v   | _ = Variable (a), v = Value (1)


Und das dann in binär Code:

Quellcode

1
2
3
assign
var 0 # 0 ist der Index im Variablen "RAM"
val_start 1 # 1 ist der Wert


Es gibt val_start und val_end um auch Strings zu erkennen. Mal sehen wie praktikabel das ist :D
Schade dass ich im Moment so wenig Zeit habe...

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

10

06.06.2014, 12:27

Nein, ich meinte, dass du Terme vor dem Parsen in die Polnische Notation überführst.
1 + 2 wird zu + 1 2 So lässt sich erheblich einfacher ein Tree erstellen, da man nicht voraussehen muss, welcher Operator die root darstellen wird.

edit: Ich verstehe immer noch nicht ganz, warum du aus _a zwei Token machst. Diese beiden Elemente hängen doch logisch zusammen?
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Architekt« (06.06.2014, 12:35)


Werbeanzeige