Die Programmiersprache Lua



Ähnliche Dokumente
Die Programmiersprache Lua. Philipp Hoppermann

Javaprogrammierung mit NetBeans. Variablen, Datentypen, Methoden

Programmieren in Lua

Lua. June 9, Marcus Brenscheidt Marcin Golkowski ( Dynamische Programmiersprachen Lua )

1. Erläutern Sie die Aufgaben von Datentypen in der imperativen Programmierung.

JavaScript. Dies ist normales HTML. Hallo Welt! Dies ist JavaScript. Wieder normales HTML.

Klassen mit Instanzmethoden

Probeklausur: Programmierung WS04/05

zu große Programme (Bildschirmseite!) zerlegen in (weitgehend) unabhängige Einheiten: Unterprogramme

JAVA-Datentypen und deren Wertebereich

Angewandte Mathematik und Programmierung

Programmieren in Java

Die Sprache C# Datentypen, Speicherverwaltung Grundelemente der Sprache. Dr. Beatrice Amrhein

Objektorientierte Programmierung. Kapitel 22: Aufzählungstypen (Enumeration Types)

Begleittext: Einführung in das Programmieren in Java für Nichtinformatiker

Inhaltsverzeichnis. Grundlagen und Einführung (1. Band) 1

Regeln von Computerspielen werden klassischerweise im Quellcode festgelegt.

Java 8. Elmar Fuchs Grundlagen Programmierung. 1. Ausgabe, Oktober 2014 JAV8

Einstieg in die Informatik mit Java

Organisatorisches. Folien (u.a.) gibt's auf der Lva-Homepage zum Download

Roberto lerusalimschy. Programmieren mit Lua

Informatik I: Einführung in die Programmierung 3. Werte, Typen, Variablen und Ausdrücke

Tag 8 Repetitorium Informatik (Java)

Vorkurs Informatik WiSe 17/18

Institut für Programmierung und Reaktive Systeme. Java 2. Markus Reschke

Mathematische Computer-Software

3.2 Datentypen und Methoden

Einführung in die Programmierung mit VBA

Vorkurs Informatik WiSe 16/17

PROCESSING EINE ZUSAMMENFASSUNG. Created by Michael Kirsch & Beat Rossmy

Die Sprache C# Datentypen, Speicherverwaltung Grundelemente der Sprache. Dr. Beatrice Amrhein

Selbsteinstufungstest Vorkurs Programmieren

1 Klassen und Objekte

Lua Grundlagen Einführung in die Lua Programmiersprache

C++ - Objektorientierte Programmierung Konstruktoren und Destruktoren

Objekte. Theorieteil. Inhaltsverzeichnis. Begriffe. Programmieren mit Java Modul 5. 1 Modulübersicht 3

Einstieg in die Informatik mit Java

1.3 Welche Schritte werden bei der Programmerstellung benötigt? 1.5 Was sind Variablen im Kontext der Programmierung?

Neben der Verwendung von Klassen ist Vererbung ein wichtiges Merkmal objektorientierter

2. Programmierung in C

Programmierung für Mathematik HS11

Java I Vorlesung Imperatives Programmieren

Javakurs FSS Lehrstuhl Stuckenschmidt. Tag 1 - Variablen und Kontrollstrukturen

Methoden. Gerd Bohlender. Einstieg in die Informatik mit Java, Vorlesung vom

Repetitorium Programmieren I + II

1 Klassen anlegen und Objekte erzeugen

1 Klassen anlegen und Objekte erzeugen

Arrays. Theorieteil. Inhaltsverzeichnis. Begriffe. Programmieren mit Java Modul 3. 1 Modulübersicht 3

C++ - Objektorientierte Programmierung Polymorphie

Felder - Arrays. Typ feldname[] = new Typ[<ganze Zahl >]; Beispiel: double vektor[] = new double[5]; auch eine Initialisierung ist möglich.

Vorlesung Programmieren

Einstieg in die Informatik mit Java

zu große Programme (Bildschirmseite!) zerlegen in (weitgehend) unabhängige Einheiten: Unterprogramme

Programmieren in C. Eine Einführung in die Programmiersprache C. Prof. Dr. Nikolaus Wulff

Algorithmen implementieren. Implementieren von Algorithmen

AuD-Tafelübung T-B5b

ÜBUNGS-BLOCK 7 LÖSUNGEN

1. Referenzdatentypen: Felder und Strings. Referenz- vs. einfache Datentypen. Rückblick: Einfache Datentypen (1) 4711 r

1. Referenzdatentypen: Felder und Strings

Algorithmik und Programmieren

Java I Vorlesung 1 Einführung in Java

Organisatorisches. Folien (u.a.) auf der Lva-Homepage Skriptum über MU Online

Ursprünge. Die Syntax von Java. Das Wichtigste in Kürze. Konsequenzen. Weiteres Vorgehen. Rund um Java

Kapitel 4. Programmierkurs. Datentypen. Arten von Datentypen. Wiederholung Kapitel 4. Birgit Engels, Anna Schulze WS 07/08

Modul 122 VBA Scribt.docx

Einstieg in die Informatik mit Java

7. Übung Informatik II - Objektorientierte Programmierung

Grundlagen und Konzepte von C Datenstrukturen

javascript Coding-Guidelines 2. Ausgabe Februar 2015 Der Guideline beschreibt den verwendeten Coding-Stil von javascript als eigene Richtline.

II.4.4 Exceptions - 1 -

Vorlesung Objektorientierte Programmierung Klausur

C++ - Objektorientierte Programmierung Konstante und statische Elemente

Probeklausur Name: (c)

Übung zur Vorlesung Wissenschaftliches Rechnen Sommersemester 2012 Auffrischung zur Programmierung in C++, 2. Teil

Einleitung Typsystem Typisierung Zusammenfassung Literatur. Typisierung. Effiziente Programmierung. Thomas Schnieders

System.out.println("TEXT");

Ausnahmen. Exceptions. Definition Ausnahmen erzeugen Ausnahmen abfangen Ausnahmen weiterleiten. Dr. Beatrice Amrhein

Objektorientiertes Programmieren (Java)

Technische Universität Braunschweig Institut für Programmierung und Reaktive Systeme

Programmiertechnik. Teil 4. C++ Funktionen: Prototypen Overloading Parameter. C++ Funktionen: Eigenschaften

12 Abstrakte Klassen, finale Klassen und Interfaces

Algorithmen und Datenstrukturen 07

Martin Unold INFORMATIK. Geoinformatik und Vermessung

Test-Klausuraufgaben Softwaretechnik Fachbereich BW, für WINFO

Die Programmiersprache C Eine Einführung

Anregungen zu Übung 2

FH D. Objektorientierte Programmierung in Java FH D FH D. Prof. Dr. Ing. André Stuhlsatz. Referenzen. Referenzen

Einfache Liste: Ein Stapel (Stack) Ansatz. Schaubild. Vorlesung 1. Handout S. 2. Die einfachste Form einer Liste ist ein Stapel (stack).

C für Java-Programmierer

2. Programmierung in C

Transkript:

Die Programmiersprache Lua Vorgelegt an: Fachhochschule Aachen, Standort Jülich Fachbereich Medizintechnik und Technomathematik Studiengang Scientific Programming Vorgelegt von: Philipp Hoppermann Matrikelnummer: 846375 Ausbildungsbetrieb: CAE Elektronik GmbH Stolberg, den 7. Dezember 2012

Inhaltsverzeichnis 1 Einleitung 1 2 Geschichte 1 3 Syntax 2 4 Datentypen und -strukturen 3 5 Kontrollstrukturen 5 5.1 Bedingte Anweisungen...................... 5 5.2 Schleifen.............................. 5 6 Funktionen 6 7 Exceptions 7 8 Coroutinen 8 9 Metatables und Metamethoden 10 10 Objektorientierung 11 10.1 Klassen.............................. 11 10.2 Vererbung............................. 12 10.3 Datenkapselung.......................... 13 11 C API 13 11.1 Stack................................ 14 11.2 Grundlagen............................ 15 11.3 Lua einbetten........................... 15 11.4 Lua erweitern........................... 16 11.5 Userdata.............................. 17 12 Standardbibliotheken 17 13 Erweiterungen 18 13.1 TkLua............................... 19 14 Compiler und virtuelle Maschine 20 15 Lua in der Spieleentwicklung 22 16 Fazit 23

2 Geschichte Die Programmiersprache Lua 1 Einleitung Die computergesteuerten Flugzeuge in den Flugsimulationen der CAE Elektronik GmbH werden über eine in C++ implementierte, künstliche Intelligenz gesteuert. Um dem Kunden die Möglichkeit zu bieten, diese künstliche Intelligenz gemäß seiner individuellen Bedürfnisse anzupassen, soll diese C++- Implementierung durch Lua ersetzt werden. Die vorliegende Seminararbeit Die Programmiersprache Lua beschreibt daher, was Lua ausmacht und sie von anderen Programmiersprachen unterscheidet. Zunächst wird die Entstehungsgeschichte Luas beleuchtet. Dann folgt ein Überblick über Syntax, Datentypen und -strukturen, Kontrollstrukturen, Funktionen und Exceptions. Das Kapitel Coroutinen zeigt, wie Multitasking mit Lua realisiert werden kann. Im Anschluss wird erläutert, weshalb Lua als extensible extension language, das heißt als erweiterbare Erweiterungssprache, bezeichnet wird. So lässt sich die Sprache über Metatables und Metamethoden erweitern, was auch Objektorientierung möglich macht. Außerdem kann Lua über die C API mit C-Programmen interagieren. Über diese API sind beispielsweise alle Lua-Standardbibliotheken programmiert. Auch mit anderen in C oder Lua entwickelten Erweiterungen kann zusätzliche Funktionalität für Lua hinzugefügt werden. Eine Besonderheit, die Lua von vielen anderen interpretierten Sprachen unterscheidet, ist ihre registerbasierte, virtuelle Maschine. Diese sowie der Lua-Compiler werden in Compiler und virtuelle Maschine näher betrachtet. Abschließend wird erläutert, weshalb Lua in der Spieleentwicklung sehr erfolgreich ist und warum sich Lua aufgrund dieses Erfolgs und ihrer einzigartigen Eigenschaften im Vergleich zu anderen Sprachen grundsätzlich auch für den Einsatz in Flugsimulationen der CAE Elektronik GmbH eignet. 2 Geschichte In den 1990er Jahren entwickelte Tecgraf, die Abteilung für Computergrafik der Pontifical Catholic University in Rio de Janeiro (PUC-Rio), Brasilien, grafische Anwendungen beispielsweise für die Öl-Firma Petrobras. Aufgrund harter Handelsbeschränkungen für Hard- und Software, war es Unternehmen nur schwer möglich, Softwarelösungen aus dem Ausland zu verwenden. Daher wurden diese von Tecgraf von Grund auf selbst entwickelt. Hierzu gehörten auch DEL (Data-Entry Language) als Eingabesprache für Simulationsdaten und SOL (Simple Object Language) als Konfigurationssprache für Graphen für Petrobras. Schnell verlangten die Nutzer, die Sprachen um über bloße Philipp Hoppermann, CAE GmbH Stolberg 1 von 23

Die Programmiersprache Lua 3 Syntax Beschreibungsfunktionalität hinausgehende Eigenschaften wie Schleifen oder bedingte Anweisungen zu erweitern. Insgesamt wurde also eine komplette Programmiersprache gefordert. 1993 diskutierten die Tecgraf-Mitarbeiter Roberto Ierusalimschy, Luiz Henrique de Figueiredo und Waldemar Celes diese neuen Anforderungen an DEL und SOL und kamen schließlich zu dem Ergebnis, dass eine einzelne, mächtigere Sprache die gestellten Anforderungen erfüllen konnte. In Frage kamen Tcl, LISP oder Scheme. Python war noch nicht weit genug entwickelt. Da die Nutzer von DEL und SOL hauptsächlich Geologen und Ingenieure waren, sollten Syntax und Semantik der Sprache möglichst einfach gehalten werden. Daher entschied man sich gegen LISP und Scheme. Außerdem sollte die Sprache plattformunabhängig laufen, um die verschiedenen Computersysteme der Tecgraf-Kunden zu unterstützen. Deshalb konnte auch Tcl, das zu diesem Zeitpunkt nur Unix unterstützte, nicht verwendet werden. Zudem konnte Tcl nicht als Datenbeschreibungssprache eingesetzt werden. Aufgrund der Handelsbeschränkungen und der daraus entstandenen doit-yourself -Mentalität beschloss man, selbst eine geeignete Sprache zu entwickeln. Diese wurde in Anlehnung an SOL, was im Portugiesischen Sonne bedeutet, Lua (portugiesisch für Mond ) getauft. Neben der bereits erwähnten Einfachheit, die eine automatische Speicherverwaltung über einen Garbage Collector einschließt, und Plattformunabhängigkeit legten die Entwickler von Anfang an großen Wert auf eine C API wie es sie bereits in SOL gegeben hatte, um Lua leicht in C-Programme einbetten zu können. Diese grundlegenden Design-Entscheidungen bildeten die Basis für eine weite Verbreitung von Lua. [IFC07] 3 Syntax Die Syntax in Lua ist möglichst einfach gehalten, damit auch Anwender ohne beziehungsweise mit nur geringer Programmiererfahrung einen leichten Zugang zu der Sprache finden können. [IFC07] So müssen aufeinanderfolgende Befehle im Lua-Code nicht durch Zeilenumbrüche oder Semikolons getrennt werden. [Ier06] Durch die Entscheidung der Entwickler für optionale Semikolons, können außerdem sowohl Fortran-Entwickler (keine Semikolons) als auch Pascal- oder C-Entwickler (Semikolons) ihre gewohnte Trennung einzelner Anweisungen beibehalten. [IFC07] Variablennamen in Lua sind beliebige Zeichenketten aus Buchstaben, Zahlen und Unterstriche. Sie dürfen jedoch nicht mit einer Zahl beginnen. Außerdem sollten Variablen nicht mit einem Unterstrich und darauf folgenden Großbuchstaben beginnen, da dieses Format für besondere Zwecke reserviert 2 von 23 Philipp Hoppermann, CAE GmbH Stolberg

4 Datentypen und -strukturen Die Programmiersprache Lua ist. Weitere reservierte Schlüsselwörter sind: and, break, do, else, elseif, end, false, for, function, if, in, local, nil, not, or, repeat, return, then, true, until und while. Da Lua generell case sensitiv ist, muss bei Variablennamen sowie Funktionsaufrufen auf Groß- und Kleinschreibung geachtet werden. Lua-Kommentare werden mit -- eingeleitet, Blockkommentare starten mit --[[ und enden mit ]]. Auch geschachtelte Kommentare sind möglich, indem Gleichheitszeichen zwischen den eckigen Klammern eingefügt werden. So geht beispielsweise der Kommentar --[=[ bis ]=] oder der Kommentar --[==[ bis ]==]. [Ier06] 4 Datentypen und -strukturen Das Prinzip der Einfachheit prägt wie die Syntax ebenso Luas Datentypen und -strukturen. Durch die Verwendung dynamischer Typisierung muss kein fester Typ für eine Variable angegeben werden, stattdessen ist der Typ einer Variable implizit durch den in ihr gespeicherten Wert gegeben. Dieser Wert ist entweder nil (ein ungültiger Wert), boolean, eine Fließkommazahl, eine Zeichenkette, eine Funktion, Userdata (siehe 11.5), ein Thread (siehe Coroutinen auf Seite 8) oder eine table. Besonders hervorzuheben ist, dass all diese Werte -auch Funktionen- first-class values sind und in Variablen gespeichert oder als Rückgabewert einer Funktion verwendet werden können. [IFC06] Zeichenketten (Strings) werden von doppelten (" ") oder einfachen ( ) Anführungszeichen eingeschlossen und können nicht verändert werden. Sie können Escape-Sequenzen wie \n oder ASCII-Codes (zum Beispiel \049 1) enthalten. Strings und Zahlen werden automatisch konvertiert, wenn arithmetische Operationen auf Strings oder Stringoperationen auf Zahlen angewendet werden. Um Strings aneinander zu hängen, wird.. verwendet. [Ier06] Die einzige Datenstruktur in Lua ist die sogenannte table - ein assoziatives Array, bei dem die Indizierung mit jedem Lua-Wert außer nil und NaN (Not a Number) erfolgen kann. In einer table können Daten verschiedenen Typs gespeichert werden, so dass sich beispielsweise Zeichenketten und Zahlen in einer table verwalten lassen. Erstellt werden tables mit adresse={plz =52062, ort=aachen}. Der Zugriff auf die Postleitzahl der table adresse lautet dann adresse[plz]. An dieser Stelle ist jedoch auch eine alternative Schreibweise zugelassen, die mehr an die Verwendung von Klassen erinnert: adresse.plz. Solche vereinfachten Schreibweisen finden sich in Lua häufig und werden von den Entwicklern syntactic sugar genannt. [IFC06] Philipp Hoppermann, CAE GmbH Stolberg 3 von 23

Die Programmiersprache Lua 4 Datentypen und -strukturen Diese tables wachsen und schrumpfen dynamisch, wenn Daten hinzugefügt oder entfernt werden. Da die Implementierung eines assoziativen Arrays nicht performant wäre, wenn es als reines Array benutzt würde, werden tables seit Lua 5.0 als hybride Datenstrukturen implementiert. Dies bedeutet, dass jede table aus einem gehashten Teil und einem reinen Array-Teil besteht. Werden aufeinanderfolgende Indizes 1 bis n verwendet, erkennt ein Algorithmus dies und setzt es intern als Array um. Alle anderen Einträge werden im gehashten Teil gespeichert. Abbildung 1 auf Seite 4 zeigt die Implementierung einer table mit den Einträgen x 9,2, 1 100, 2 200, 3 300. Header Hash-Teil x 9,2 nil Array-Teil 100 200 300 nil Abbildung 1: Lua table [IFC05] Diese Implementierung unterscheidet Lua beispielsweise von Perl. Der Befehl $a[1000]=1 erzeugt in Perl ein Array mit tausend Elementen. Der Lua-Befehl a={[1000]=1} hingegen erzeugt eine Lua-table mit einem Eintrag im gehashten Teil und einem leeren Array-Teil. Somit ist Lua hier wesentlich speichereffizienter. Generell optimiert die Implementierung von table-einträgen als Array sowohl die Geschwindigkeit, da für den Array-Teil kein Hashing nötig ist, als auch den Speicherbedarf, da im Array-Teil keine Schlüssel gespeichert werden müssen. Außerdem sorgt eine spezielle Hashfunktion dafür, dass bei der Sondierung keine Kollisionen zweiter Ordnung auftreten. So kann ein Füllgrad von 100% ohne Performanceverlust erreicht werden. Im Vergleich mit einer Lua-Implementierung mit einer Umsetzung von tables als rein assoziative Arrays zeigt sich in Testfällen von The Great Computer Language Shootout das verbesserte Laufzeitverhalten mit Array- Optimierung. Verglichen werden die Laufzeiten der Tests in Sekunden auf einem Testrechner mit einem Intel Pentium IV und 512MB Arbeitsspei- 4 von 23 Philipp Hoppermann, CAE GmbH Stolberg

5 Kontrollstrukturen Die Programmiersprache Lua cher unter Linux 2.6. Zusätzlich sind die Laufzeiten für die hybride table- Implementierung relativ zur rein assoziativen Implementierung aufgelistet. [IFC05] Test rein assoziativ hybrid sieve (100) 0,82 0,57 (70%) heapsort (5e4) 1,05 0,70 (67%) matrix (50) 0,82 0,59 (72%) Tabelle 1: Vergleich table mit und ohne Array-Optimierung [IFC05] 5 Kontrollstrukturen Der Lua-Code ist in Blöcke unterteilt, wobei ein Block eine Kontrollstruktur, eine Funktion oder eine Reihe aufeinanderfolgender, mit do end umschlossener Befehle sein kann ist. Innerhalb eines Blocks können lokale Variablen mit dem Schlüsselwort local erzeugt werden. Diese sind dann nur in diesem Block gültig. [Ier06] 5.1 Bedingte Anweisungen Bedingte Anweisungen haben die Form: i f Ausdruck then a else b end Das Ergebnis der Auswertung von Ausdruck darf ein beliebiger Wert sein. Alle Werte außer false und nil werden als wahr ausgewertet. Dies gilt insbesondere auch für 0. Werden Vergleichsoperatoren verwendet, vergleicht Lua tables, Userdata und Funktionen anhand ihrer Referenz, Zahlen und Zeichenketten anhand ihres Werts. Der Kleiner- und Größer-Operator kann nur auf Zahlen und Zeichenketten angewendet werden, der Gleichheitsoperator (==) und der Ungleichheitsoperator (~=) auf beliebige Werte. Um bedingte Anweisungen zu schachteln, wird das Schlüsselwort elseif verwendet. [Ier06] 5.2 Schleifen In Lua können kopfgesteuerte Schleifen while Ausdruck do a end, fußgesteuerte Schleifen Philipp Hoppermann, CAE GmbH Stolberg 5 von 23

Die Programmiersprache Lua 6 Funktionen repeat a until und Zählschleifen for Ausdruck i=startwert, Endwert, S c h r i t t w e i t e do a end genutzt werden. Der Parameter Schrittweite der for-schleife ist optional. Wird er weggelassen, ist die Schrittweite 1. Die Ausführung der Schleifen kann mit break abgebrochen werden. Abgesehen von der Verwendung als einfache Zählschleife, kann die for- Schleife in Lua auch mit Iteratoren verwendet werden. Mit der Funktion pairs, die alle Schlüssel-Wert Paare einer table zurückgibt, kann eine table wie folgt traversiert werden: for s,w in p a i r s ( t a b l e ) do p r i n t ( s.. " ".. w) end [Ier06] 6 Funktionen Lua-Funktionen bestehen aus einem Namen, einer Parameterliste und einem Funktionskörper. Funktionen, in deren Parameterliste... steht, können beliebig viele Argumente (varargs) verarbeiten. Auf diese Argumente kann innerhalb der Funktion über... zugegriffen werden. So weist local a,b=... den Variablen a und b die ersten beiden Argumente der varargs zu. Mit {...} kann eine table mit allen Argumenten erstellt werden. Beim Aufruf einer Funktion kann unabhängig von der Parameterliste der Funktion immer eine beliebige Anzahl Argumente übergeben werden. Werden weniger Argumente angegeben als erwartet, werden die übrigen Argumente mit nil besetzt. Überschüssige Argumente werden ignoriert. Auf diese Weise können auch default-werte für einzelne Parameter angegeben werden. function defaultwerte ( param ) param=param or " d e f a u l t "... end Lua initialisiert param zunächst mit nil. Or liefert das erste Argument, wenn dieses zu wahr ausgewertet wird, und sonst das zweite Argument. Wird die Funktion defaultwerte ohne Argumente aufgerufen, wird param=n i l or " d e f a u l t " 6 von 23 Philipp Hoppermann, CAE GmbH Stolberg

7 Exceptions Die Programmiersprache Lua ausgewertet. Da nil falsch ist, erhält param den Wert "default". Lua-Funktionen können beliebig viele Werte zurückgeben. Diese werden kommasepariert im return-statement aufgelistet: return wert1, wert2 Um mehrere Rückgabewerte zu verwenden, werden Mehrfachzuweisungen der Form a,b=f() genutzt. Gibt f() mehr Werte zurück als Variablen angegeben sind, werden die überschüssigen Rückgabewerte ignoriert. Gibt f() weniger Werte zurück als Variablen angegeben sind, werden die zusätzlichen Variablen auf nil gesetzt. Als Übergabewert können Funktionen anonym definiert werden. Ein Beispiel hierfür ist die Sortierfunktion für tables, die als zweiten Parameter eine Funktion erhält, anhand derer sortiert werden soll. t a b l e. s o r t ( adressen, function ( a, b ) return ( a. p l z > b. p l z ) end) Das Beispiel sortiert die table adressen, die mehrere Adressen enthält, nach der Postleitzahl. Tatsächlich sind Funktionen jedoch nicht nur als Übergabewert sondern immer anonym. Der Aufruf print(ausgabe) ruft daher strenggenommen nicht die Funktion print auf, sondern greift auf die Variable print zu, der die entsprechende Funktion zugewiesen ist. Aus diesem Grund können Funktionen neu definiert werden. So lässt sich beispielsweise der Zugriff auf eine bestimmte Funktionalität beschränken und die eigene Lua-Umgebung auf spezielle Bedürfnisse anpassen, was unter anderem Sandbox-Umgebungen ermöglicht. [Ier06] 7 Exceptions Aufgrund ihres vorwiegenden Einsatzes als Erweiterungssprache darf Lua nicht abstürzen oder sich einfach beenden. Daher wird im Fehlerfall ein Error ausgelöst und zur Host-Anwendung zurückgekehrt. Errors treten auf, wenn beispielsweise versucht wird, arithmetische Operationen mit Strings durchzuführen. Außerdem kann ein Error auch mit error(fehlermeldung) selbst ausgelöst werden. Mit assert kann die Überprüfung, ob ein Fehler aufgetreten ist, und das Auslösen eines Errors wie folgt zusammengefasst werden: z=a s s e r t ( tonumber ( n ), " keine Zahl " ) Philipp Hoppermann, CAE GmbH Stolberg 7 von 23

Die Programmiersprache Lua 8 Coroutinen Hier wird versucht, n in eine Zahl umzuwandeln. Ist dies nicht möglich, wird ein Error mit der (optionalen) Fehlermeldung keine Zahl ausgelöst. Tritt kein Fehler auf, wird das Ergebnis in z gespeichert. Um den so erzeugten Error zu behandeln, wird die Funktion, die den Error auslöst, mit i f p c a l l ( f ) then... else... end aufgerufen. Verursacht f einen Fehler, liefert pcall (protected call) false und die Fehlermeldung zurück, ansonsten true. Insgesamt entspricht die Verwendung von error(fehlermeldung) und pcall in etwa dem aus Java bekannten throw, try... catch. [Ier06] 8 Coroutinen In Lua 5.0 wurden asymmetrische Coroutinen eingeführt, um Multitasking mit Lua zu ermöglichen. Hierbei ist jeder Coroutine ein eigener Stack zugeordnet, der vom Interpreter beiseite gelegt wird, wenn eine andere Coroutine gestartet wird. Nicht länger benutzte Stacks werden vom Garbage Collector eingesammelt. [IFC05] Asymmetrisch bedeutet, dass es verschiedene Funktionen zum Unterbrechen und Starten einer Coroutine gibt. Coroutinen sind vergleichbar mit Threads in dem Sinne, dass sie einen eigenen Ausführungsstrang darstellen und eigene lokale Variablen besitzen, globale Variablen jedoch mit anderen Coroutinen teilen. Allerdings wird mit Coroutinen kooperatives Multitasking realisiert. Im Gegensatz zu Threads läuft immer nur eine Coroutine gleichzeitig. Diese muss ihre Ausführung selbstständig beenden oder anhalten. Mit coroutine.create wird ein thread erzeugt. Dabei wird als Parameter eine Funktion übergeben, die von der Coroutine ausgeführt werden soll. Um die Coroutine zu starten wird coroutine.resume aufgerufen. Dadurch wechselt die Coroutine ihren Status von suspended zu running. Ist die Funktion der Coroutine ausgeführt worden, ändert sich ihr Status zu dead. Durch den Aufruf von coroutine.yield innerhalb der von der Coroutine ausgeführten Funktion wird die Ausführung dieser Funktion unterbrochen und die Coroutine wechselt in den suspended Status. coroutine.resume startet die Ausführung der Coroutine wieder. Abgesehen von der Möglichkeit, die Ausführung einer Funktion zu unterbrechen und weiterlaufen zu lassen, können mit yield und resume auch Werte übergeben werden. So liefert resume sämtliche Argumente zurück, die dem entsprechenden yield übergeben werden. Ebenso liefert yield sämtliche Argumente zurück, die dem entsprechenden resume übergeben werden. 8 von 23 Philipp Hoppermann, CAE GmbH Stolberg

8 Coroutinen Die Programmiersprache Lua Außerdem werden die return-werte der Coroutinen-Funktion an das aufrufende resume zurückgegeben. Im folgenden Beispiel wird die Funktion der Coroutine co zunächst mit den Argumenten 1 und 2 aufgerufen. Anschließend wird die Kontrolle per yield wieder an das Hauptprogramm zurückgegeben, bevor dieses die Coroutine mit resume weiterlaufen lässt. Abschließend liefert die Coroutine die return-werte 6 und 7 zurück, sodass das Programm insgesamt die Zahlen 1 bis 7 in der richtigen Reihenfolge ausgibt resume gibt zusätzlich zu den übergebenen Argumenten einen Boolean zurück, der signalisiert, ob ein Fehler aufgetreten ist. Abbildung 2 auf Seite 9 zeigt, wie in dem Beispiel die Kontrolle über den Programmablauf und Argumente zwischen Hauptprogramm und Coroutine übergeben werden. co=c o r o u t i n e. c r e a t e ( function ( a, b ) p r i n t ( a, b ) 1 2 p r i n t ( c o r o u t i n e. y i e l d ( 3, 4 ) ) 5 return 6,7 end) p r i n t ( c o r o u t i n e. resume ( co, 1, 2 ) ) t r u e 3 4 p r i n t ( c o r o u t i n e. resume ( co, 5 ) ) t r u e 6 7 [Ier06] :Hauptprogramm co:coroutine resume(co,1,2) yield(3,4) resume(co,5) return 6,7 Abbildung 2: Daten- und Kontrollübergabe bei Coroutinen Philipp Hoppermann, CAE GmbH Stolberg 9 von 23

Die Programmiersprache Lua 9 Metatables und Metamethoden 9 Metatables und Metamethoden Soll in Lua Funktionalität implementiert werden, die standardmäßig nicht möglich ist und einen Error auslösen würde, können sogenannte Metatables verwendet werden. Mit Hilfe dieser Metatables kann das Verhalten eines beliebigen Datentyps bei nicht definierten Operationen gesteuert werden. In Lua selbst ist jedoch nur die Definition von Metatables für tables möglich. Für alle anderen Datentypen muss C-Code verwendet werden. Eine Metatable kann jede beliebige table inklusive der table, für die die Metatable gesetzt wird, selbst sein und auf eine einzelne table oder mehrere tables angewendet werden. Auf diese Weise lassen sich etwa mathematische Vektoren mit tables realisieren. Sollen beispielsweise die tables v1 und v2, die n-dimensionale Vektoren repräsentieren, addiert werden, würde zunächst eine entsprechende Metatable meta mit dem Feld add definiert. Dieses Feld definiert das Verhalten für v1+v2, eine sogenannte Metamethode. Wird nun meta als Metatable für v1 und v2 gesetzt, können diese tables addiert werden und liefern als Ergebnis die table erg={5,7,9}. function addvektor ( a, b) local tmp={} for index, wert in i p a i r s ( a ) do tmp [ index ]= wert+b [ index ] end return tmp end meta={ add=addvektor } v1 ={1,2,3} v2 ={4,5,6} setmetatable ( v1, meta ) setmetatable ( v2, meta ) erg=v1+v2 Weiterhin können Metamethoden für Multiplikation, Subtraktion, Division, Negation, Modulo, Potenz, Konkatenation oder auch tostring und Vergleichsoperationen implementiert werden. Außerdem kann das setzen einer Metatable unterbunden werden, indem das Feld metatable gesetzt wird. Die Metamethoden index und newindex werden aufgerufen, wenn auf ein Feld einer table zugegriffen beziehungsweise ein Feld geändert wird. Mit einer geeigneten Implementierung lassen sich auf diese Weise nur lesbare tables oder auch Objektorientierung realisieren. Zu Objektorientierung siehe 10 von 23 Philipp Hoppermann, CAE GmbH Stolberg

10 Objektorientierung Die Programmiersprache Lua Kapitel 10 auf Seite 11. [Ier06] 10 Objektorientierung Da alle Werte in Lua first-class values sind und daher in tables gespeichert werden können, kann eine table als Objekt betrachtet werden. Um Attribute und Methoden zu realisieren, werden Werte und Funktionen in einer table abgespeichert. In einer Funktion wird die aufrufende table dann über einen Parameter (im Beispiel self) identifiziert, so dass innerhalb der Funktion auf ihre Attribute zugegriffen werden kann. function f ( s e l f, parameter ) s e l f. xy =... end objekt. methode=f Auch an dieser Stelle haben die Entwickler jedoch syntactic sugar genutzt, um die Implementierung intuitiver zu gestalten: function objekt : f ( parameter ) s e l f. xy =... end Der Aufruf von f erfolgt dann entweder mit objekt. f ( objekt, parameter ) oder objekt : f ( parameter ) Der Doppelpunkt ersetzt sowohl bei der Definition als auch beim Aufruf der Funktion die explizite Übergabe des self Parameters. Obwohl auf diese Weise Attribute und Methoden erstellt werden können, unterscheidet sich diese Art von Objekten deutlich von Objekten, wie sie in C++ oder Java vorkommen. Zum einen werden keine Klassen erstellt, sondern Werte und Funktionen direkt Objekten zugewiesen. Zum anderen ist es so nicht möglich, eine Datenkapselung zu implementieren. [IFF96] 10.1 Klassen Obwohl Lua das Konzept von Klassen nicht kennt, kann gemeinsame Funktionalität für mehrere Objekte über Prototypen implementiert werden. Diese Prototypen sind Objekte, deren einziger Zweck es ist, Funktionalität, die von mehren Objekten ( Instanzen ) genutzt werden soll, an einer Stelle abzulegen. Philipp Hoppermann, CAE GmbH Stolberg 11 von 23

Die Programmiersprache Lua 10 Objektorientierung Um die table prototyp als Prototyp für die table instanz zu nutzen, wird für instanz eine Metatable gesetzt, die die Metamethode index (vergleiche Metatables und Metamethoden auf Seite 10) implementiert. setmetatable ( instanz, { index=prototyp }) Wird nun auf ein Feld in instanz zugegriffen, das dort nicht vorhanden ist, sorgt die index-metamethode dafür, dass das Feld in prototyp gesucht wird. Mit instanz:f() wird so prototyp.f(instanz) ausgeführt. Da auch die table prototyp selbst als Metatable genutzt werden kann, ist es möglich, eine Art Konstruktor zu verwenden: function prototyp : new ( ) neueinstanz={} setmetatable ( neueinstanz, s e l f ) s e l f. index=s e l f return neueinstanz end i n s t a n z=prototyp : new ( ) [Ier06] 10.2 Vererbung Das Prinzip der Realisierung von Klassen über Prototypen kann auch für Vererbung genutzt werden, indem über die index-metamethode jeweils auf die Basisklasse verwiesen wird. a b g e l e i t e t e r P r o t o t y p=prototyp : new ( ) a b g e l e i t e I n s t a n z=a b g e l e i t e t e r P r o t o t y p : new ( ) Beim Aufruf abgeleiteterprototyp:new() ist self abgeleiteterprototyp. Das bedeutet, der index-eintrag in der Metatable von abgeleitete- Instanz zeigt jetzt auf abgeleiteterprototyp und der index-eintrag in der Metatable von abgeleiteterprototyp auf prototyp. Auf diese Weise wird die Vererbungshierarchie auf der Suche nach der gewünschten Funktion oder dem gewünschten Attribut so lange zurückverfolgt, bis das Gesuchte gefunden worden oder die Vererbungshierarchie zu Ende ist. Mehrfachvererbung kann realisiert werden, indem alle Basisklassen in einer table gespeichert werden. Über den index-eintrag der Metatable wird dann die Klasse gesucht, die das gewünschte Feld enthält. [Ier06] 12 von 23 Philipp Hoppermann, CAE GmbH Stolberg

11 C API Die Programmiersprache Lua 10.3 Datenkapselung Lua ist als Erweiterungssprache nicht dafür ausgelegt, für große Projekte verwendet zu werden. Daher fehlen Schlüsselwörter wie private, protected und public, mit denen Zugriffsberechtigungen geregelt werden könnten. Dennoch ist es über eine Art Proxy möglich, den Zugriff auf bestimmte Felder und Funktionen zu verhindern. function z a e h l e r ( s t a r t w e r t ) local s e l f ={wert=s t a r t w e r t } end local function hoch ( ) s e l f. wert=s e l f. wert+1 end local function getwert ( ) return s e l f. wert end return { hoch=hoch, getwert=getwert} In diesem einfachen Beispiel ist ein Zähler implementiert, der im Konstruktor einen startwert erhält und diesen mit hoch hochzählen kann. Die Funktion zaehler gibt eine table zurück, die die Funktionen hoch und getwert enthält. Der direkte Zugriff auf wert wird so unterbunden. Benutzen lässt sich dieser Zähler wie folgt: z=z a e h l e r ( 1 ) z. hoch ( ) p r i n t ( z. getwert ( ) ) An dieser Stelle muss erwähnt werden, dass es sich bei den Feldern hoch und getwert der zurückgegebenen table strenggenommen nicht um Funktionen, sondern um Closures handelt. Diese Closures enthalten nicht nur die Funktionen, sondern auch die Variablen der umschließenden Funktion (im Fall von zaehler die Variable self). Bei einem neuen Aufruf von zaehler wird eine neue Closure mit einer neuen self-variablen erzeugt. [Ier06] 11 C API Lua wird von ihren Entwicklern als extensible extension language bezeichnet, da Lua einerseits als Bibliothek in C-Programmen verwendet (extension) Philipp Hoppermann, CAE GmbH Stolberg 13 von 23

Die Programmiersprache Lua 11 C API und andererseits durch C-Funktionen erweitert werden kann (extensible). Daher ist die C API das Herzstück von Lua. Dies gilt selbst dann, wenn dem Programmierer die Nutzung der API nicht bewusst ist, da er vermeintlich ein eigenständiges Lua-Programm entwickelt. Als eingebettete Sprache ist Lua jedoch nicht ohne ein einbettendes Programm lauffähig, weshalb es sich bei dem Lua-Interpreter de facto um ein kleines C-Programm handelt, das als Host-Anwendung fungiert. Auch bei der Nutzung der Standardbibliotheken wird implizit die API genutzt, da diese über die C API implementiert sind. [Ier06] Abgesehen von der C API existieren auch APIs für andere Programmiersprachen wie beispielsweise Fortran, Java, Smalltalk, Ada, C#, Perl oder Ruby. [IFC07] 11.1 Stack Als Verbindungsstück zwischen C und Lua muss die C API zwei grundsätzliche Unterschiede beider Sprachen überwinden. Dies sind zum einen die statische Typisierung in C im Vergleich zur dynamischen Typisierung in Lua und zum anderen die manuelle Speicherverwaltung in C im Vergleich zur automatischen Speicherverwaltung über einen Garbage Collector in Lua. Hierzu verwendet die C API einen abstrakten Stack. Ruft Lua eine C-Funktion auf, wird ein neuer Stackframe mit den Argumenten der Funktion auf den Stack gelegt. Am Ende der Funktion legt diese ihre Rückgabewerte dann ebenfalls auf den Stack. Die Lua-Werte auf dem Stack können von beziehungsweise in C-Werte(n) umgewandelt werden, falls es einen entsprechenden C-Datentyp gibt. Dies ist beispielsweise bei Strings oder Zahlen der Fall. Andere Lua Werte (wie tables und Funktionen) können nicht in einen äquivalenten C-Typ konvertiert werden. Deshalb müssen zum Beispiel table-felder einzeln ausgelesen werden. Auf diese Weise ist das Problem der unterschiedlichen Typisierung gelöst. Das Problem der Speicherverwaltung wird behoben, indem Werte auf dem Stack niemals vom Garbage Collector eingesammelt werden. So kann es nicht passieren, dass Speicherbereiche freigegeben werden, die im C-Code noch verwendet werden. Erst wenn eine C-Funktion beendet ist, wird der entsprechende Stackframe vom Stack entfernt. Die im C-Code verwendeten Lua-Werte werden also nicht mehr verwendet und unterliegen nun wieder der automatischen Speicherverwaltung. [IFC07] 14 von 23 Philipp Hoppermann, CAE GmbH Stolberg

11 C API Die Programmiersprache Lua 11.2 Grundlagen Sämtliche Informationen über den Zustand des Lua-Interpreters werden in einem lua_state gespeichert. [IFC06] Ein Zeiger auf diesen Zustand wird allen Funktionen in Lua übergeben, wodurch diese eintrittsinvariant werden. [Ier06] Dies bedeutet, dass sie gleichzeitig von beliebig vielen Threads unabhängig ausgeführt werden können. [Wik12a] Deshalb kann Lua erst für Multithreading genutzt werden. Als notwendige Umgebung für die Ausführung einer Lua-Funktion muss im C-Code zunächst mit lual_newstate ein neuer Zustand erzeugt werden. Dieser wird dann den API-Funktionen als Argument übergeben. Gemäß dem Prinzip, nur benötigte Bibliotheken zu verwenden und damit Speicherplatz zu sparen, enthält der so erzeugte Zustand keine vordefinierten Funktionen. Mit lual_openlibs können die Standardbibliotheken nachgeladen werden. [Ier06] 11.3 Lua einbetten Wird Lua als Datenbeschreibungssprache etwa für Konfigurationsdateien genutzt oder soll das C-Programm mit Lua erweitert werden, muss Lua in C eingebettet werden. Hierzu wird der abstrakte Stack genutzt. Um eine Lua-Funktion im C-Code zu nutzen, werden zunächst die Funktion und ihre Argumente auf den Stack gelegt. Dann wird die Funktion ausgeführt. Abschließend können die Rückgabewerte der Funktion vom Stack gelesen werden. Die Lua Funktion function a d d i e r e ( a, b ) return a+b end wird im C-Code mit dem lua_state L wie folgt ausgeführt: lua_getglobal (L, " a d d i e r e " ) ; lua_pushnumber (L, 1 ) ; lua_pushnumber (L, 2 ) ; lua_pcall (L, 2, 1, 0 ) ; double e r g e b n i s=lua_tonumber (L, 1); lua_pop (L, 1 ) ; Wenn Lua über die C API eingebunden wird, muss beachtet werden, dass Fehler auftreten können. Hierbei handelt es sich meistens um Speicherallokierungsfehler. Im Fehlerfall wird eine panic function aufgerufen, die standardmäßig die Anwendung beendet. Soll dies nicht geschehen, kann diese Philipp Hoppermann, CAE GmbH Stolberg 15 von 23

Die Programmiersprache Lua 11 C API Funktion über lua_atpanic auch selbst gesetzt werden. Für gewöhnlich wird jedoch der Lua-Code mit lua_pcall abgesichert ausgeführt. Dies bedeutet, dass selbst bei Speicherallokierungsfehlern ein Fehlercode zurückgegeben wird und der Interpreter in einem gültigen Zustand verbleibt. [Ier06] Der Aufruf lua_pcall erhält als Parameter den lua_state, die Anzahl der Funktionsparameter, die Anzahl der Rückgabewerte und den Stackindex einer Funktion zur Fehlerbehandlung (0 bedeutet, es wird die originale Fehlermeldung zurückgeliefert). [IFC06] Die Funktion lua_tonumber liefert das Ergebnis der Addition als Double zurück. Der Zugriff auf den Stack erfolgt entweder über negative (von oben) oder positive (von unten) Indizes. -1 ist also das oberste Element im Stack. Zum Schluss wird das Ergebnis der Addition mit lua_pop vom Stack entfernt. [Ier06] 11.4 Lua erweitern Sollen in Lua C-Funktionen genutzt werden, sollte ein C-Modul implementiert werden. Dieses C-Modul enthält die gewünschten Funktionen in der Form static int f(lua_state* L). Der int Rückgabewert gibt die Anzahl der Rückgabewerte an, da es in Lua mehrere Rückgabewerte geben kann. Zeiger auf die Funktionen des Moduls werden dann gemeinsam mit einem Namen in einem Array gespeichert: s t a t i c const struct lual_reg meinmodul [ ] = { {" meinefunktion ", f }, {NULL, NULL} } ; Das Paar {NULL, NULL} signalisiert das Ende der Aufzählung. Schließlich werden die Funktionen über int luaopen_meinmodul ( lua_state L){ l u a L _ r e g i s t e r (L, meinmodul, meinmodul ) ; return 1 ; } registriert. Bei der Registrierung wird in Lua eine table meinmodul erstellt, welche als Einträge die Paare aus Funktionsname und Funktion hat. Die umschließende Funktion luaopen_modulname ist nötig, da Lua beim Einbinden eines Moduls versucht, diese Funktion aufzurufen. Nachdem das Modul als dynamische Bibliothek gebaut wurde, kann es mit require in den Lua-Code eingebunden und dort wie eine gewöhnliche Lua table verwendet werden. 16 von 23 Philipp Hoppermann, CAE GmbH Stolberg

12 Standardbibliotheken Die Programmiersprache Lua Wird Lua mit C-Funktionen erweitert, sollten diese im Fehlerfall lua_error (oder lual_error für eine formatierte Fehlermeldung) aufrufen. Auf diese Weise werden ungültige Zustände des Interpreters vermieden. Stattdessen kehrt der Lua-Code mit der Fehlermeldung zum aufrufenden lua_pcall zurück. [Ier06] 11.5 Userdata Mit Hilfe der C API und sogenannter Userdata lassen sich C-Datentypen auch in Lua nutzen. Dies kann sinnvoll sein, um sowohl im Hinblick auf Speicherals auch Rechenzeitbedarf effizientere Datentypen benutzen zu können. Bei Userdata handelt es sich um Speicherbereiche, in denen beliebiger Inhalt abgelegt werden kann. Über lua_newuserdata und lua_touserdata kann ein neues Userdatum angelegt beziehungsweise ein bestehendes Userdatum erhalten werden. Beide Funktionen liefern einen void* zurück, der in den gewünschten C-Typ gecastet wird. C-Funktionen, mit denen ein Userdatum bearbeitet werden kann, werden mit lual_register unter einem bestimmten Namen im Lua-Code bekannt gemacht. Würde in C ein Array implementiert, könnte es sich bei diesen Funktionen beispielsweise um Funktionen handeln, die den Wert für einen bestimmten Index zurückliefern oder setzen. Da C-Module sich wie Lua tables verhalten, können über Metatables Operatoren für Userdata definiert werden, sodass etwa auf das in C definierte Array wie gewohnt mit a[i] zugegriffen werden könnte. [Ier06] 12 Standardbibliotheken Lua ist modular aufgebaut. So kann zusätzliche Funktionalität leicht hinzugefügt oder ausgetauscht werden, ohne den Sprachkern zu verändern. Zudem muss eine Anwendung nur die tatsächlich benötigten Bibliotheken mitliefern. Viele häufig verwendete Funktionen finden sich thematisch zusammengefasst in den Lua-Standardbibliotheken. Diese sind mit der C API implementiert, so dass performance-kritische Funktionen wie Sortierfunktionen schneller ausgeführt werden können. Der Aufruf von Bibliotheksfunktionen erfolgt über b i b l i o t h e k. funktion ( argumente ) Die string-bibliothek bietet Funktionen, mit denen Zeichenketten beispielsweise formatiert oder reguläre Ausdrücke genutzt werden können. Da für Strings eine Metatable gesetzt ist, deren index-eintrag auf die string- Bibliothek zeigt, sind hier auch Aufrufe in der objektorientierten Form Philipp Hoppermann, CAE GmbH Stolberg 17 von 23

Die Programmiersprache Lua 13 Erweiterungen s : funktion ( argumente ) möglich. Die table-bibliothek kann genutzt werden, um etwa tables zu sortieren, einen Eintrag zu entfernen oder die table als String-Repräsentation zu erhalten. Mathematische Funktionen wie sin, min, exp oder random finden sich in der math-bibliothek. Ein- und Ausgabefunktionalität liefert die I/O-Bibliothek. Die Funktionen dieser Bibliothek können entweder implizit (io.close(file)) oder explizit (file:close()) aufgerufen werden. Als file können auch die vordefinierten io.stdin, io.stdout und io.stderr in der aus C bekannten Bedeutung verwendet werden. Über die os-bibliothek können Funktionen zum Beispiel zur Zeitmessung, Ausführung von Kommandozeilen-Befehlen oder zum Löschen oder Umbenennen von Dateien und Ordnern ausgeführt werden. In der debug-bibliothek finden sich diverse Funktionen, die Debug-Funktionalität für Lua bieten. Da hierfür auch Informationen beispielsweise über lokale Variablen benötigt werden, werden von dieser Bibliothek einige Lua- Regeln missachtet. Ihre Benutzung kann daher zu eigentlich ungültigen Zugriffen führen und sollte auch aus Geschwindigkeitsgründen rein zu Debugging-Zwecken eingesetzt werden. [IFC06] 13 Erweiterungen Abgesehen von den Standardbibliotheken gibt es diverse Erweiterungen, die unterschiedlichste Funktionalitäten für Lua bereit stellen. Diese können wie die Standardbibliotheken über die C API oder als Module in Lua programmiert werden. Diese Module werden als table implementiert und mit r e q u i r e "modul" eingebunden. Um ein Lua-Skript als Modul zu nutzen, ist es ausreichend, am Anfang der Lua-Datei module(...) beziehungsweise module(..., package.seeall), falls externe Module innerhalb des eigenen Moduls genutzt werden sollen einzufügen. Mehrere zusammengehörige Module werden zu einem Package zusammengefasst. [Ier06] Diese in Lua oder C entwickelten Erweiterungen ermöglichen das Erstellen graphischer Benutzeroberflächen, Bild- und Textverarbeitung, die Verwendung von Ton, Netzwerkanwendungen wie beispielsweise Browser oder E-Mail-Programme und Chat-Clients, Datenbankanbindungen, Dateisystem- 18 von 23 Philipp Hoppermann, CAE GmbH Stolberg

13 Erweiterungen Die Programmiersprache Lua operationen, die Verwendung mathematischer Bibliotheken und vieles mehr. [Lib12] Im Folgenden wird anhand der Anbindung von Tk an Lua beispielhaft erläutert, wie externe Bibliotheken die Möglichkeiten von Lua erweitern und Lua so für neue Einsatzgebiete nutzbar machen. 13.1 TkLua Tk ist ein plattformübergreifendes Toolkit, mit dem grafische Benutzeroberflächen erstellt werden können. Es wurde für Tcl (Tool Command Language) entwickelt, ist mittlerweile aber auch von Perl, Python, Ruby und anderen Sprachen aus verwendbar. [Wik12c] Auch Lua bietet eine Tk-Anbindung. Aus Sicht der Entwickler sei die Möglichkeit, Lua als Datenbeschreibungssprache zu nutzen, gut geeignet, um die Anordnung der Bedienelemente abzuspeichern. Für die Behandlung von Aktionen mit der Benutzeroberfläche biete sich Lua ebenfalls an. [IFC96] Tatsächlich werden in anderen Bibliotheken verschiedene Sprachen genutzt, um diese beiden Bereiche der Oberflächenentwicklung (Anordnung und Verhalten) zu definieren. So verwendet Qt XML-Dateien, um das Aussehen der Oberfläche abzuspeichern, und C++, um die Reaktion auf Benutzeraktionen zu beschreiben. [Wik12b] Die Idee, Lua für Oberflächenentwicklung zu nutzen, liegt also nahe, da hier beide Bereiche mit einer Sprache realisiert werden können. Um die Benutzung von Tk mit Lua einfach zu gestalten, wurden die Bezeichnungen für Bedienelemente (Widgets), Attribute und Befehle aus Tk übernommen und auf Lua-Widgets umgesetzt. Diese können so wie gewöhnliche tables benutzt werden. Beispielsweise wird ein Label mit dem Text Text mit l=tklabel{text = "Text"} erzeugt. Für jedes Widget wurde ein Standardattribut definiert (bei Label zum Beispiel text). Dadurch ist auch folgende Schreibweise möglich: l=t k l a b e l {"Text"} Um die Beschriftung zu ändern, wird l.text="neuer Text" verwendet. Um dieses Label anzuzeigen, wird es auf ein Fenster gelegt: f e n s t e r=tkmain{ l } w: show ( ) Benutzeraktionen werden mit Callbacks realisiert. Ein Button, der den Text des Labels ändert, wird mit b=tkbutton {"Text ändern "} function b : command ( arg ) l. t e x t = "Text geändert " Philipp Hoppermann, CAE GmbH Stolberg 19 von 23

Die Programmiersprache Lua 14 Compiler und virtuelle Maschine end implementiert. Die übergebene table arg enthält alle Tk-Argumente sowie einen Verweis auf die aufrufende Instanz (über self). [Cel12] Außer der Tk-Anbindung gibt es für Lua auch Erweiterungen, die die Verwendung weiterer GUI-Bibliotheken wie wxwidgets, GTK, Open Motif oder Qt ermöglichen. [Lua12] 14 Compiler und virtuelle Maschine Als in Clean C 1 programmierte Bibliothek ist Lua nur eingebettet in ein Host- Programm lauffähig. Dieses einbettende Programm ruft Lua-Funktionen auf und kann Lua-Variablen lesen und setzen und C-Funktionen registrieren, die aus Lua aufgerufen werden. Daher wird im Lua-Paket ein Host-Programm mitgeliefert, welches als Interpreter Lua auch stand-alone lauffähig macht. [IFC06] Code, der von diesem Interpreter verarbeitet werden kann, muss zunächst compiliert werden. Der Compiler liefert die Befehle (Opcodes) on the fly an die virtuelle Maschine. Für eingebettete Lua-Programme kann der Lua-Code auch vorcompiliert werden. In diesem Fall kann der Compiler, der etwa 30% der Größe des Lua-Core ausmacht, weggelassen und durch ein kleines Modul ersetzt werden, welches die Binärdateien lädt. So lässt sich die Größe des ohnehin schon kleinen Lua-Pakets noch weiter reduzieren. Hieran zeigt sich, wie viel Wert auf billiges Einbetten von Lua gelegt wird. Der Lua-Compiler versucht, die an ihn gestellten Anforderungen an Größe, Geschwindigkeit und Qualität miteinander zu kombinieren. Ein schneller Compiler ist besonders wichtig, da Lua als Datenbeschreibungssprache oft mehre Megabyte große Dateien verarbeiten muss. Gleichzeitig sollen Lua- Programme schnell laufen, weshalb der Compiler den Code im Rahmen der plattformunabhängig realisierbaren Verbesserungen für die virtuelle Maschine optimiert. Seit Lua 5.0 (2003) nutzt Lua eine registerbasierte, virtuelle Maschine. Das bedeutet, dass alle lokalen Variablen in Registern gespeichert werden und der Zugriff auf diese daher sehr effizient möglich ist. Bei einer stackbasierten, virtuellen Maschine müssten die Variablen auf dem Stack bewegt werden, um gelesen oder geschrieben zu werden. Dies wäre in Lua besonders aufwendig, da Werte als sogenannte tagged unions gespeichert werden. Diese bestehen aus einem Integer-Tag zur Identifikation des Typs und dem Wert. Auf einer 1 Der Begriff Clean C stammt aus C - a Reference Manual [HS02] und bezeichnet eine Untermenge von ANSI C, die zugleich gültiger C++-Code ist. [Cle] 20 von 23 Philipp Hoppermann, CAE GmbH Stolberg

14 Compiler und virtuelle Maschine Die Programmiersprache Lua 32bit Maschine mit 64bit langen Double-Werten benötigt eine tagged union 12 Byte. Auf weitere Optimierungen der internen Repräsentation der Werte wurde an dieser Stelle verzichtet, um die ANSI C Kompatibilität zu wahren. Die Operanden bei einer registerbasierten, virtuellen Maschine müssen im Befehl angegeben werden, weshalb die Befehle einer registerbasierten, virtuellen Maschine mit vier Byte im Vergleich zu Befehlen bei einer stackbasierten, virtuellen Maschine (typischerweise ein oder zwei Byte) deutlich länger sind. Da die Befehle zum Bewegen der Variablen auf dem Stack entfallen, ist der compilierte Code insgesamt jedoch kaum länger. Allerdings müssen die Operanden aus dem Befehl dekodiert werden, was zusätzlichen Overhead für den Interpreter bedeutet. Dieser Overhead besteht jedoch nur aus einfachen, zum Beispiel logischen, Operationen und steht im Vergleich zum Laden von Operanden, die nicht im Befehl gespeichert werden, in einer stackbasierten, virtuelle Maschine. Die meisten Befehle der virtuelle Maschine haben ein Format mit drei Adressen. So entspricht der Befehl a=a+i dem Opcode ADD x x y. Hierbei ist x das Register mit der lokalen Variablen a und y das Register mit der lokalen Variable i. Durch dieses Befehlsformat mit drei Adressen kann der Opcode stark verkleinert werden. Dies zeigt sich am Beispiel a=a+i, wie es in Lua 4.0 umgesetzt worden ist: GETLOCAL x GETLOCAL y ADD SETLOCAL x Häufig genutzte Werte, wie die Register oder lokale Variablen, sind als Array implementiert, was einen schnellen Zugriff auf diese ermöglicht. Im Vergleich mit einer stackbasierten, virtuellen Maschine zeigt eine Lua-Implementierung mit einer registerbasierten, virtuellen Maschine eine zum Teil deutlich verbesserte Performance in Testfällen, die abgesehen vom sum - Test, bei dem Integer von 1 bis n summiert werden, von The Great Computer Language Shootout stammen. Die Tests wurden auf einem Testrechner mit einem Intel Pentium IV und 512MB Arbeitsspeicher unter Linux 2.6 ausgeführt. Tabelle 2 auf Seite 22 vergleicht die Laufzeiten für eine Lua- Implementierung mit stackbasierter beziehungsweise registerbasierter virtueller Maschine sowie die relative Laufzeit, die den Geschwindigkeitszuwachs besonders verdeutlicht. [IFC05] Philipp Hoppermann, CAE GmbH Stolberg 21 von 23

Die Programmiersprache Lua 15 Lua in der Spieleentwicklung Test stackbasiert registerbasiert sum (2e7) 1,23 0,54 (44%) fibo (30) 0,95 0,68 (72%) ack (8) 1,00 0,86 (86%) random (1e6) 1,04 0,96 (92%) sieve (100) 0,93 0,82 (88%) heapsort (5e4) 1,08 1,05 (97%) matrix (50) 0,84 0,82 (98%) Tabelle 2: Vergleich stackbasierte und registerbasierte, virtuelle Maschine [IFC05] 15 Lua in der Spieleentwicklung In den ersten Jahren nach ihrer Entwicklung wurde Lua fast ausschließlich von Tecgraf genutzt. Dann verwendete jedoch Bret Mogilefsky, der Chef- Programmierer von Grim Fandango, einem LucasArts Adventure aus dem Jahr 1998, Lua bei der Entwicklung dieses Spiels. Nachdem Mogilefsky Lua auf der Games Developer s Conference einem breiten Spieleentwickler-Publikum vorgestellt hatte, fand Lua rasch weite Verbreitung besonders in der Spieleindustrie. Bekannte Spiele, die Lua nutzen, sind beispielsweise Far Cry, The Sims und World of Warcraft. Dies überraschte die Entwickler zunächst, da Lua für ein eher wissenschaftliches Einsatzgebiet entwickelt worden war. Bei genauerer Betrachtung zeigte sich allerdings für Roberto Ierusalimschy, Luiz Henrique de Figueiredo und Waldemar Celes, weshalb Lua sich offensichtlich besonders gut für die Spieleentwicklung eignet: Generell ist bei der Spieleentwicklung die zukünftige Weiterentwicklung einer Sprache nicht so wichtig wie bei anderer Software, da meist ein Spiel als fertiges Produkt entwickelt wird. Neuerungen fließen eher in ein neues Spiel ein statt über Updates ausgeliefert zu werden. Es ist also ausreichend, wenn sich eine Sprache für das aktuelle Spiel eignet, was die Bereitschaft erhöht, neue Sprachen zu testen. Da Lua seit der Version 5.0 unter MIT Lizenz steht und Open Source ist, kann es auch in kommerziellen Anwendungen verwendet und an die jeweiligen, speziellen Bedürfnisse angepasst werden. Diese Bedürfnisse richten sich an eine multitasking-fähige Skriptsprache, mit der die Spiele-Engine und die Skripte von Charakteren oder Ereignissen getrennt werden können. Mit der C API kann Lua in die meist in C++ programmierte Engine eingebettet werden. Viele Anwender der Skriptsprache sind Game- und Level-Designer und keine professionellen Programmierer, 22 von 23 Philipp Hoppermann, CAE GmbH Stolberg

16 Fazit Die Programmiersprache Lua für die die einfache Syntax und Semantik Luas, die ursprünglich für die Ingenieure bei Petrobras entwickelt worden war, wichtig ist. Abgesehen von dieser Verwendung als Skriptsprache eignet sich Lua auch, um Daten in einem einheitlichen Format abzulegen. Hier profitiert Lua von ihren Anfängen in der Datenbeschreibungssprache SOL. Oft werden Spiele sowohl für PC als auch für Konsolen entwickelt. Durch die Implementierung Luas in ANSI C ist Lua plattformunabhängig und kann auch auf Konsolen compiliert werden. Besonders für Konsolen sind auch die Systemanforderungen der verwendeten Sprache wichtig, da hier sowohl Speicherplatz als auch Rechenleistung beschränkt sind. Je effizienter und kleiner die verwendete Sprache also ist, desto mehr Ressourcen stehen für andere Teile des Spiels zur Verfügung. Der Lua-Core benötigt gerade einmal 100Kb Speicherplatz. [IFC07] Zudem zeigen Benchmarks, dass Lua eine der schnellsten Skriptsprachen verglichen beispielsweise mit Ruby und Python ist. [Ful12] Außerdem kann Nutzern mit Lua die Möglichkeit gegeben werden, vergleichsweise leicht eigene Add-ons für ein Spiel zu erstellen. Eine entsprechende API stellt zum Beispiel World of Warcraft zur Verfügung. [Emm09] 16 Fazit Die Verwendung Luas als Skriptsprache in der Spieleindustrie, die Lua insbesondere auch zur Implementierung künstlicher Intelligenz nutzt, ist vergleichbar mit der geplanten Anwendung der CAE Elektronik GmbH, das Verhalten der computergesteuerten Flugzeuge mit Lua zu steuern. Daher treffen viele Aspekte, die im Kapitel Lua in der Spieleentwicklung (15) dargestellt sind, auch auf die Simulationssoftware der CAE Elektronik GmbH zu. So sind die geplanten Anwender der Skripte für die künstliche Intelligenz ebenfalls keine professionellen Programmierer, sondern Kunden, die das Verhalten der Simulation anpassen können möchten. Auch muss Lua über die C API in die in C++ programmierte Simulation eingebettet werden. Diese läuft in Echtzeit, weshalb es wichtig ist, eine schnelle Skriptsprache zu haben. Benchmarks belegen, dass Lua wesentlich schneller ist als vergleichbare Sprachen. Dennoch bleibt fraglich, ob die im Vergleich zur aktuellen C++-Implementierung erhöhte Rechenzeit [Ful12] den Anforderungen der Simulation genügt. Philipp Hoppermann, CAE GmbH Stolberg 23 von 23

Literatur [Cel12] Celes, Waldemar: Tklua - Binding Tk to Lua. http://www. tecgraf.puc-rio.br/~celes/tklua/. Version: Oktober 2012 [Cle] Clean C. http://lua-users.org/lists/lua-l/2010-06/ msg00491.html [Emm09] Emmerich, Paul: Beginning Lua with World of Warcraft Add-Ons. Apress, 2009 [Ful12] [HS02] [Ier06] [IFC96] [IFC05] [IFC06] [IFC07] [IFF96] Fulgham, Brent: Computer Language Benchmarks Game, The. http://shootout.alioth.debian.org/. Version: September 2012 Harbison, Samuel P. ; Steele, Guy L. Jr.: C: A Reference Manual. Bd. 5. Prentice Hall, 2002. ISBN 0 13 089592 X Ierusalimschy, Roberto: Programming in Lua. Roberto Ierusalimschy, 2006. ISBN 85 90 37982 5 Ierusalimschy, Roberto ; Figueiredo, Luiz H. ; Celes, Waldemar: Lua: an Extensible Embedded Language. In: Dr. Dobb s Journal 21 (1996), Nr. 12, 26œ33. http://www.lua.org/ddj.html Ierusalimschy, Roberto ; Figueiredo, Luiz H. ; Celes, Waldemar: Implementation of Lua 5.0, The. In: Journal of Universal Computer Science 11 (2005), Nr. 7, S. 1159 1176 Ierusalimschy, Roberto ; Figueiredo, Luiz H. ; Celes, Waldemar ; Lua.Org, PUC-Rio (Hrsg.): Lua Reference Manual 5.1. Lua.Org, PUC-Rio, 2006. http://www.lua.org/manual/5. 1/manual.html Ierusalimschy, Roberto ; Figueiredo, Luiz H. ; Celes, Waldemar: Evolution of Lua, The. In: Proceedings of the Third ACM SIGPLAN Conference on History of Programming Languages ACM, 2007, 2 1 Ierusalimschy, Roberto ; Figueiredo, Luiz H. ; Filho, Waldemar C.: Lua-an Extensible Extension Language. In: Software Practice and Experience 26 (1996), Nr. 6, 635 652. http: //www.lua.org/spe.html

[Lib12] Libraries And Bindings. http://lua-users.org/wiki/ LibrariesAndBindings. Version: Oktober 2012 [Lua12] Graphical User Interface Toolkits. http://lua-users.org/wiki/ GraphicalUserInterfaceToolkits. Version: Oktober 2012 [Wik12a] Wikipedia: Eintrittsinvarianz. (2012), Oktober. http://de. wikipedia.org/wiki/eintrittsinvarianz [Wik12b] Wikipedia: Qt (Bibliothek). (2012), Oktober. http://de. wikipedia.org/wiki/qt_(bibliothek) [Wik12c] Wikipedia: Tk (Toolkit). (2012), Oktober. http://de. wikipedia.org/wiki/tk_(toolkit)