Code Generierungstechniken
• Code Munger
• Inline code expander
• Mixed code generation
• Partial class generation
• Full Tier or layer generation
• Domain specific language
Code Munging
• Funktionsweise:
• Input ist Quellcode
• Analysieren der Eingabedatei und Generierung von Output
• Einsatzbereiche:
• Programmdokumentation (Extraktion von Kommentaren)
• Erstellung eines Index für Klassen/Methoden
• Quellcode analysieren
• Filter
• Anwendungsbeispiele:
• JavaDoc
• XDoclet (Java Annotation)
Quellcode
Ausgabedatei
Code Munger
• weitere Beispiele
• Generierung eines Klassen-/Methodenindex in HTML
• Generierung von Basisklassen
• Generierung von SQL-Statements aus CSV-Datei
• Generierung von Basisklassen aus DDL-Statements
• Quellcode Analyse
• Modifikation von XML-Dokumenten (ohne XSLT/DOM)
• ...
Beispiel 1
• Welche Klassen und Methoden gibt es in PEAR/MDB2.php?
$ grep -E '^\s*(function|class)' d:/Programme/xampp/php/PEAR/MDB2.php class MDB2
function setOptions(&$db, $options) function classExists($classname)
function loadClass($class_name, $debug) function &factory($dsn, $options = false) function &connect($dsn, $options = false)
function &singleton($dsn = null, $options = false) function loadFile($file)
function apiVersion()
function &raiseError($code = null, $mode = null, $options = null, $userinfo = null) function isError($data, $code = null)
function isConnection($value) ...
class MDB2_Error extends PEAR_Error
function MDB2_Error($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN,
$level = E_USER_NOTICE, $debuginfo = null) class MDB2_Driver_Common extends PEAR
function __construct()
Ausgabeformatierung in HTML
$ grep -E '^\s*(function|class)' d:/Programme/xampp/php/PEAR/MDB2.php | sed -r 's#(class \w+.*)\s+\w+#<h1>\1</h1>#;s#function &?(\w+)(.*)# \
<p><b>\ \ method \1</b>\2#'
<h1>class MDB2</h1>
<p><b> method setOptions</b>(&$db, $options) <p><b> method classExists</b>($classname)
<p><b> method loadClass</b>($class_name, $debug) <p><b> method factory</b>($dsn, $options = false) <p><b> method connect</b>($dsn, $options = false)
<p><b> method singleton</b>($dsn = null, $options = false) <p><b> method loadFile</b>($file)
<p><b> method apiVersion</b>()
<p><b> method raiseError</b>($code = null, $mode = null, $option = null,
$userinfo = null)
<p><b> method isError</b>($data, $code = null) <p><b> method isConnection</b>($value)
<p><b> method isResult</b>($value)
<p><b> method isResultCommon</b>($value) <p><b> method isStatement</b>($value)
<p><b> method errorMessage</b>($value = null) <p><b> method parseDSN</b>($dsn)
<p><b> method fileExists</b>($file)
• Pattern matching (egrep-syntax)
• Pattern substitution ( s # text # relacement # ) (extended sed-syntax)
^\s*(function|class)\s+\w+
start of line
zero or more whitespaces
one or more whitespaces or
s#function\s+&?(\w+)(.*)#<p><b> Method \1</b>\2</h4>#
one or more whitespaces
one or more alphanumeric characters
store the characters inside ( ) into variable<x>zero or more characters (the rest of the line)
Analyse regulärer Ausdruck
one or more alphanumeric characters
optional &-character
Output
MDB2.php
MDB2.index.html
MDB2.html
class DB </pre><h2><a name="432">class DB</a></h2><pre>
</pre><b><a name= "592">function isError($value)</a></b><br/><pre>
function isError($value)
rest ...
class MDB2 <h2><a href="DB.html#432">class DB</a></h2>
<b><a href= "DB.html#592">function isError($value)</a></b><br/>
function isError($value)
<pre> ... </pre> around whole document
Einfache Erweiterung mittels Frameset
• MDB2.index.html • MDB2.html
• Overall effort:
• 2 regular expressions for index (one line each)
• 2 regular expressions for code (one line each)
• 3 command line calls
• 1 frameset
easily adaptable
for many
languages !!
Code Munger - Beispiel (2)
File: Person.php
<?php
include 'BasePerson.class.php';
class Person extends BasePerson { // @fields: name, day_of_birth function age() {
$now = time();
$birthday = strtotime($this->day_of_birth);
return floor(($now -$birthday)/(3600*24*365));
} }
<?php
class BasePerson { protected $name;
protected $day_of_birth;
function __construct() { }
function get_name() { return $this->name;
}
function get_day_of_birth() { return $this->day_of_birth;
}
function set_name($value) { return $this->name = $value;
}
function set_day_of_birth($value) { return $this->day_of_birth = $value;
}
Example (Implementation)
File: generate-base-classes.php
<?php
$lines = file($argv[1]);
foreach ($lines as $line) {
if (preg_match('#^\s*class\s+\w+\s+extends\s+(\w+)\s*{#', $line, $match)) { $base_class = $match[1];
} else if (preg_match('#^\s*//\s*@fields\s*:\s*(.*)#',$line,$match)) { $attributes = preg_split('#\s*,\s*#', $match[1]);
$content = create_base_class($base_class, $attributes);
file_put_contents($base_class.".class.php", $content);
}
} generate class definition
write class definition to file look for the start of a class definition
look for the line with the @fields keyword
function create_base_class($classname, $attributes) { $content = "<?php\n
class $classname {\n";
foreach ($attributes as $attribute) {
$content .= " protected \$$attribute;\n";
}
$content .= "\n function __construct() { }\n";
foreach ($attributes as $attribute) {
$content .= "\n function get_$attribute() { return \$this->$attribute;
}\n";
}
foreach ($attributes as $attribute) {
$content .= "\n function set_$attribute(\$value) { return \$this->$attribute = \$value;
}\n";
}
$content .="\n}";
return $content;
}
instance variables
getter
setter
Run the Test
• File: simple_test.php
<?php
include 'Person.php';
print "a little test:\n";
$p = new Person();
$p->set_name('Schmidt');
$p->set_first_name('Andreas');
$p->set_day_of_birth('9 September 1965');
print $p->get_first_name()." ".
$p->get_name()." is likely ".
$p->age()." years old\n";
$ php.exe generate-base-classes.php Person.php
$ php.exe simple_test.php a little test:
Andreas Schmidt is likely 46 years old
example: add an attribute to an xml-document
Concrete: add attribute stereotype with value entity to element class
• Solution 1: use XSLT
• Solution 2: use DOM or SAX
• Simplest solution: perl regular expression substitution from the command line
$ perl -pi.bak -e 's/(<class)(.*?)/\1 stereotpe="entity" \2/' my-model.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE my-model SYSTEM "my-meta.dtd">
<my-model name="DEMO">
<class name="Film">
<attribute name="title" type="String"/>
<attribute name="year" type="Integer"/>
...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE my-model SYSTEM "my-meta.dtd">
<my-model name="DEMO">
<class stereotype="entity" name="Film">
<attribute name="title" type="String"/>
<attribute name="year" type="Integer"/>
...
Inline Code Expansion
• Funktionsweise:
• Impliziert die Definition einer eigenen (einfachen) Sprache
• Vereinfacht Quellcodeerstellung
• Generierter Code ist Ausgangspunkt für Compilierung
• Einsatzbereiche:
• Embedded SQL
• Embeded assembler sections
• Embedded mathematical equations
• Generierung von Basisklassen
Quellcode
Ausgabequellcode Inline Code
Executable Expander
Compiler
Beispiel Inline Code Expansion
• Source code my-cinema.phpi
<?php
<class: Person (surname, first_name, day_of_birth) >
<class: Film (title, year, director) >
$p1 = new Person('Waits', 'Tom', '9.9.1949');
$f1 = new Film('Short Cuts', 1989, 'Jim Jarmusch');
echo "a little test:\n---\n";
echo $p1->get_surname()." ".$p1->get_first_name()." ".
$p1->get_date_of_birth()."\n";
echo $f1->get_title()." ".$f1->get_year()."\n";
?>
Inline Code Expander
Beispiel Inline Code Expansion
<?php
class Person { ... } class Film {
private $title;
private $year;
private $director;
function __construct($title,$year,$director) { $this->title = $title;
$this->year = $year;
$this->director = $director;
}
function get_title() { return $this->title;
}
function get_year() { return $this->year;
}
function get_director() { return $this->director;
} }
$p1 = new Person('Waits', 'Tom', '9.9.1949');
$f1 = new Film('Short Cuts', 1989, 'Jim Jarmusch');
echo "a little test:\n---\n";
echo $p1->get_name()." ".$p1->get_first_name()." ".
$p1->get_day_of_birth()."\n";
echo $f1->get_title()." ".$f1->get_year()."\n";
#<class:\s*(\w+)\s*\((.*)\)\s*>#
<class: Person (surame, first_name, date_of_birth) >
store content inside ( ) for later use zero or more whitespaces
Pattern Matching for Extended Syntax
Codefragment Inline Code Expander
• inline-code-expander.php
<?php
$lines = file($argv[1]);
foreach ($lines as $line) {
if (preg_match('#<class:\s*(\w+)\s*\((.*)\)\s*>#', $line, $match)) {
$class_name = $match[1];
$att_list = preg_split("#\s*,\s*#", $match[2]);
print_class_definition($class_name, $att_list);
} else {
print $line;
} }
look for a line with special syntax
otherwise, do nothing (just output the line) (extended language syntax)
<class: Person (name,vorname,geburtsdatum) >
/<class:\s*(\w+)\s*\((.*)\)\s*>/
$match[1] $match[2]
Beispiel: Regulärer Ausdruck
function print_class_definition($class_name, $att_list) { ?>
class <?php echo $class_name ?> {
<?php foreach ($att_list as $a) { ?>
private $<?php echo $a ?>;
<?php } ?>
function __construct(<?php echo join(",",add_dollar_sign($att_list)) ?>) { <?php foreach ($att_list as $a) { ?>
$this-><?php echo $a ?> = $<?php echo $a ?>;
<?php } ?>
}
<?php foreach ($att_list as $a) { ?>
function get_<?php echo $a ?>() { return $this-><?php echo $a ?>;
}
<?php } ?>
}
<?php }
Example Workflow
$ php.exe inline-code-expander.php example-source.phpi > gen_ice.php
$ php.exe gen_ice.php a little test:
---
Waits Tom 9.9.1949 Short Cuts 1989
weiteres Beispiel ...
Datei: literturliste.pls
#! perl -w use strict;
use DBI;
<fifdb:gehoim@literatur_db,
select titel, autor from literatur>
Datei: literaturliste.pl
#! perl -w use strict;
use DBI;
my $dbh = DBI->connect('dbi:Oracle:literatur_db', 'fifdb',
'gehoim' ) ||
die "Database connection not made: $DBI::errstr";
my $sql = "select titel, autor from literatur";
my $sth = $dbh->prepare( $sql );
$sth->execute();
while (my @row = $sth->fetchrow()) { print join(",", @row)."\n";
}
$sth->finish();
$dbh->disconnect();
Mixed Code Generation
• Eigenschaften:
• Code als Input
• „In Place“ Modifikation (Input Datei = Output Datei)
• praktische Implementierung des Inline Code Expanders
• „Special Markup“ (meist Kommentar) wird transformiert
• Einsatzgebiete
• wie Inline Code Expander
• Erzeugung von get/set Methoden
• Erzeugung von „Infrastrukturcode“
• ...
Quellcode
Quellcode
Executable Compiler Mixed Code
Generator
Beispiel: Mixed Code Generation
class person {
protected $id;
private $name;
private $vorname;
function __construct($id, $name, $vorname) {
$this->id = $id;
$this->name = $name;
$this->vorname = $vorname;
}
// get($id) // get($name) // set($name) // set($vorname) // get($vorname)
class person { ...
function get_id() { return $this->id;
}
function get_name() { return $this->name;
}
function set_name($name) { $this->name = $name;
}
function set_vorname($vorname) { $this->vorname = $vorname;
}
function get_vorname() { return $this->vorname;
}
Implementierung Beispiel
• Implementation with regular expressions in perl (as command line tool)
• call:
perl -pi.bak transformation.pl source.php
• file: transformation.pl
•
s#^(\s*)//\s*set\(\$(\w+)\)#
$1function set_$2(\$$2) {
$1 \$this->$2 = \$$2;
$1}#;
s#^(\s*)//\s*get\(\$(\w+)\)#
$1function get_$2() {
$1 return \$this->$2;
$1}#;
# explanations:
# $1: indent (whitespaces)
# $2: name of variable
# \$: print a $-sign
# \(: matches a (-sign
Ersetzung mittels regulärere Ausdrücke
s#^(\s*)//\s*set\(\$(\w+)\)#$1function set_$2(\$$2) {
$1 \$this->$2 = \$$2;
$1}#;
// set($name)
function set_name($name) { $this->name = $name;
}
Partial Class Generator Model
• Funktionalität:
• basiert auf explizitem Modell
• generiert Reihe von Klassen
• Manuelle Erweiterungen in abgeleiteten Klassen oder „protected areas“
• Ausgangspunkt für die Entwicklung eines „Tier Generators“
• Anwendungsbeispiele:
• data access layer
• Database scheme
• User interfaces
• Import/export filters
Partial Class Generator Model
Definition file Templates
Compiler
Executable (model)
Output Base Class source code
hand written source code Partial Tier
Generator
Modelldefinition
# Definition of a simple Terra-Modell
#
$c = new MClass('Stadt');
$c->addAttribute('name');
$c->addAttribute('einwohner');
$c->addAttribute('lage');
$c->addAttribute('laenge');
$c->addAttribute('breite');
$classes[] = $c;
$c = new MClass('Fluss');
$c->addAttribute('name');
$c->addAttribute('laenge');
$c->addAttribute('schiffbar');
$classes[] = $c;
$c = new MClass('Berg');
$c->addAttribute('name');
$c->addAttribute('hoehe');
$c->addAttribute('erstbesteigung');
$classes[] = $c;
Implementierung des Metamodells
class MClass { public $name;
public $attributes;
function __construct($name) {
$this->name = $name;
}
function addAttribute($name) {
$this->attributes[]=new MAttribut($name);
}
function attribute_names() {
foreach ($this->attributes as $a) $names[] = '$'.$a->name;
return $names;
} }
class MAttribut { public $name;
function __construct($name) {
$this->name = $name;
} }
Beispiel Template
<? include 'model.php'; ?>
<? foreach ($classes as $class) { ?>
class CRUD_<?= $class->name ?> { // instance variables
<? foreach ($class->attributes as $a) { ?>
private $<?= $a->name ?>;
<? } ?>
function __construct(<?= join(",", $class->attribute_names()) ?>) { <? foreach ($class->attributes as $a) { ?>
$this-><?= $a->name ?> = $<?= $a->name ?>;
<? } ?>
}
// the rest of the implementation follows here ;-) // ... (yes, here)
<? } ?>
generierter Code
class CRUD_Stadt {
// instance variables private $name;
private $einwohner;
private $lage;
private $laenge;
private $breite;
function __construct($name,$einwohner,$lage,$laenge,$breite) { $this->name = $name;
$this->einwohner = $einwohner;
$this->lage = $lage;
$this->laenge = $laenge;
$this->breite = $breite;
}
// the rest of the implementation follows here ;-) // ... (yes, here)
}
class CRUD_Fluss {
// instance variables private $name;
private $laenge;
private $schiffbar;
function __construct($name,$laenge,$schiffbar) { $this->name = $name;
$this->laenge = $laenge;
$this->schiffbar = $schiffbar;
}
• Integration der Applikationslogik in Unterklassen
class Stadt extends CRUD_Stadt { //Application logic comes here ...
}
class Fluss extends CRUD_Fluss { //Application logic comes here ...
}
class Berg extends CRUD_Berg {
//Application logic comes here ...
}
• Template für Stubs
# Stub generator for the application classes
<? foreach ($classes as $class) { ?>
class <?= $class->name ?>
extends CRUD_<?= $class->name ?> { //Application logic comes here ...
}
<? } ?>
Erweiterung um Applikationslogik
Tier Generator Model
• Eigenschaften
• Wird aus abstraktem Modell generiert
• Erstellt den kompletten Code einer Schicht (Tier)
• Prinzip analog Partial Class Generator
• Einsatzgebiete
• Datenbankzugriffsschicht
• Web Client Layer
• Data Import/Export
• ...
Definition file Templates
Compiler
Executable (model)
Output Base Class source code
Generator Tier
Tier Generator Model
• Unterschied zu Partial Class Generator:
• Erzeugt den kompletten Code einer Anwendungsschicht
• Gesamte Applikationslogik liegt außerhalb der „Codebase“
• Validierung der Applikationslogik durch Domänenexperten möglich
• Durch Änderung der Templates kann die gesamte Anwendung auf eine andere Plattform portiert werden
• Partial Class Generator ist sehr guter Ausgangspunkt für die Implementierung eines Tier generators
• Partial Class Generator folgt der 80/20 Prozent Regel
80% des Codes werden vom Partial Class Generator erzeugt 20% des Codes in abgeleiteten Klassen manuell erstellt.
• Full Tier Generator ist dann später auch für die letzten 20% des Codes verantwortlich
Domänenspezifische Sprachen
• Eigenschaften:
• Eigene Sprache für spezielles Anwendungsgebiet
• Dient zur Implementierung der Geschäftslogik
• Werkzeuge zur Erstellung von DSL:
• Lexikalische Analyse: LEX, FLEX
• Programmgeneratoren (legen Grammatik fest): YACC, BISON, ANTLR
• Beispiele:
• Mathematica
• SQL
• make, ant
(werden in Vorlesung nicht weiter verfolgt !)
Edit
Test Compile
Edit
Test Compile Edit Templates
Generate
Code Generierungs Workflow
• Traditionelle Softwareentwicklung • Softwareentwicklung mit Generator
Exkurs: reguläre Ausdrücke
• Mächtige Sprache zur Beschreibung von Textmustern
• in vielen Sprachen (PHP, Perl, Java, ...), Werkzeugen (sed, grep, awk, ...) und Editoren (vi, emacs, ...) implementiert
• Sonderzeichen:
• Die meisten zeichen repräsentieren sich selbst, es gibt jedoch eine Reihe von Zeichen mit Sonderbedeutung: \ | ( ) [ ]{} ^ $ * + ? . /
• Soll nach einem dieser Zeichen im Text gesucht werden, so muss ihnen ein Backslash \ vorangestellt werden, z.B. \$ für die Suche nach einem Dollarzeichen
• Bedeutungen der Sonderzeichen siehe nächste Folie
• reguläre Zeichen werden i.a. durch das /-Zeichen eingeschlossen, z.B.
/MDSD/
Zeichen Bedeutung Beispiel
^ Anfang der Zeichenkette ^Hallo: Zeichenkette, die mit dem Wort ’Hallo’ beginnen
$ Ende der Zeichenkette Amen$: Zeichenkette, die mit dem Wort ’Amen’ endet
. beliebiges Zeichen (außer Zeilenumbruch) X.L: Zeichenketten wie ’XML’, ’XSL’, ’X4L’, ’X!L’, ’X.L’.
[ ] Zeichenklasse: Passt auf genau ein Zeichen im Text.
Um welche Zeichen es sich handelt wird innerhalb der Klammer spezifiziert.
Es gibt eine Reihe von bereits vordefinierten Zei- chenklassen (siehe weiter unten)
[AEIOUaeiou]: Passt auf alle Vokale [A-Z]: Passt auf alle Großbuchstaben
[^A-Z]: alle Zeichen die keine Großbuchstaben sind [-_]: Die Zeichen - und _
* Wiederholung: der links vom Zeichen stehende Aus- druck, kann null bis n-mal auftreten (habgierig)
A*: ’’, ’A’, ’AA’, ’AAA’, ...
[a-z]*: Zusammenhängende Kette von null bis n Kleinbuch- staben
.*: alles + Wiederholung: der links vom Zeichen stehende Aus-
druck, kann ein bis n-mal aufreten (habgierig)
? Optional: der links vom Fragezeichen stehende Aus- -?[0-9]+: Eine Zahl, die sowohl positiv als auch negativ sein
*? Wiederholung: der links vom Zeichen stehende Ausdruck, kann null bis n-mal auftreten (nicht habgierig)
A*: ’’, ’A’, ’AA’, ’AAA’, ...
[a-z]*: Zusammenhängende Kette von null bis n Klein- buchstaben
.*: alles +? Wiederholung: der links vom Zeichen stehende
Ausdruck, kann ein bis n-mal aufreten (nicht habgierig)
| Alternative A|a: entweder ’A’ oder ’a’
( ) Gruppierung/Speicher: Grupiert n-Zeichen, speichert den Inhalt der Klammer in Variable ($1, $2, ...)
Tel: ([-/ 0-9]) : Speichert die Telefonnummer (bestehnd aus den Zeichen 0-9, Leerzeichen sowie ’-’ und ’/’) in der Variable $1 (bzw. \1)
(Herr|Frau) : Entweder ’Herr’ oder ’Frau’
([0-9]+),\1 : passt, wenn eine Zahl zweimal (durch Komma getrennt) hintereinander steht
{<min>[, [<max>]]} Kardinalität: Der Ausdruck links der öffnenen Klammer musss mindestens <min>-mal Auftre- ten und maximal <max>-mal.
[0-9]{3-5} : drei bis fünfstellige Zahlen [0-9]{5} : Fünfstellige Zahlen
x{10,}: mindestens zehn mal ’x’
\s Leerzeichen: passt auf <Space>, <TAB>.
<Return>, <FormFeed>,
^\s*#.*: passt auf Zeilenkommentare (z.B. in PHP, Perl)
\w Wortzeichen, Buchstaben, Zahlen und Underline (_)
\w+
\S Negation von \s
\W Negation von \w
\b Wortgrenze (0 Zeichen) \bdie\b: ’ die ’ aber nicht ’ diebisch’