php.de

Zurück   php.de > Webentwicklung > Software-Design

Software-Design Diskussionen auf Profi-Niveau: PHP Lösungen auf konzeptioneller Ebene

Antwort
 
LinkBack Themen-Optionen Bewertung: Bewertung: 1 Stimmen, 5,00 durchschnittlich.
Alt 07.01.2011, 21:20  
Moderator
 
Benutzerbild von Chriz
 
Registriert seit: 11.05.2008
Beiträge: 6.269
Chriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer Anblick
Standard StringParser bauen, wie setz ich den Lexer um

Hallo,

ich bin gerade dabei einen SQL-Lexer(Scanner?)/Parser zu bauen, eigentlich nur, weil ich einen OR-Mapper (für mich) baue, der auch DDL lesen können soll. Und zwar richtig und nicht (ausschließlich/hauptsächlich) per RegExp.

Kurzer Quellcodeausschnitt:

Anti_StringParser übernimmt das Parsen (von einem einfachen CREATE TABLE-Statement) anhand der Token-Definition von Anti_StringParser_Mysql. Als Identifier habe ich u.a. einfach mal eine RegExp-Klasse benutzt, die bestimmte Zeichenketten als zusammengehörig erkennt. Die Problematik besteht jetzt eigentlich wenn variable Codeteile (SQL-Identifier, also Tabellennamen oder sogar Strings) kommen:
PHP-Code:
<?php
class Anti_StringParser
{
    
// ..

    
public function parse($source// z.B. = "CREATE TABLE .."
    
{
        
$position 0;
        
$source   = (string)$source;
        
$length   mb_strlen($source); // Länge, damit ich weiß wann zu Ende geparset ist
        
$tokens   = array(); // meine Liste der erzeugten Tokens
        
$maxLoop  10000// infinite loop Schutz ;)
        
do {
            
$current mb_substr($source$position); // relevanter Quellcodeabschnitt
            
$found   false;
            foreach (
$this->_identifiers as $name => $identifier) {
                
// durchlaufe alle Identifier (siehe nächstes Quellcodebeispiel für die Definitionen)
                
list ($match$offset$token) = $identifier->matches($current$source$position); // hat ein Identifier einen Token gefunden?
                
if (!$match) {
                    continue; 
// nicht? dann frag den nächsten
                
}
                
$found     true;
                
$position += $offset// OK offset auf die aktuelle Position rechnen, damit das nächste Stück Code gelesen werden kann
                
$tokens[]  = $token;
                break;
            }
            if (!
$found) {
                
// keiner Identifier hat was gefunden, Quellcode wurde nicht erkannt/ist nicht gültig
                
throw new Exception("unknown identifier at position [$position]:\n<var>$current</var>");
            }
            
            
$parsed = ($position == $length); // sind wir fertig?
            
            
if (--$maxLoop <= 0) {
                throw new 
Exception("infinite loop at position [$position]: $current");
            }
        } while (!
$parsed);
        
        
Debug::stop($tokens); // ~ var_dump
    
}
}
?>
PHP-Code:
<?php
class Anti_StringParser_Mysql extends Anti_StringParser
{
    public function 
init()
    {
        
$this->_addIdentifier('regexp''whitespace''\s+'); // whitespace Erkennung per RegExp
        
$this->_addIdentifier('regexp''keyword',    '\b[A-Za-z][A-Za-z0-9\_]*\b'); // Keyword erkennung
        
$this->_addIdentifier('char',   '(');
        
$this->_addIdentifier('char',   ')');
        
$this->_addIdentifier('char',   '=');
        
$this->_addIdentifier('char',   ';');
        
$this->_addIdentifier('char',   ',');
        
$this->_addIdentifier('regexp''string',     "'[^']*'"); // Strings, erstmal ganz einfach
        
$this->_addIdentifier('regexp''number',     "\b[0-9]+\b"); // Zahlen (für z.B. "int(10)")
        
$this->_addIdentifier('regexp''identifier'"`[^`]*`"); // DB-Identifier (nicht zu verwechseln mit dem Klassentyp, der die Tokens erstellt), doofe Namenswahl vlt.
    
}
}
?>
PHP-Code:
<?php
class Anti_StringParser_Identifier_RegExp extends Anti_StringParser_Identifier_Abstract
{
    
// ..
    
    
public function matches($current$source$position)
    {
        try {
            
$pattern $this->_delimiter '^' $this->_regExp $this->_delimiter $this->_modifiers// regexp bauen
            
$success = (bool)preg_match($pattern$current$matches);
            if (!
$success) {
                return array(
falsenullnull); // nix gefunden
            
}
            
$match   $matches[0];
            
$offset  mb_strlen($matches[0]); // offset berechnen
            
$token   = new Anti_StringParser_Token($this->_name$match); // token bauen
            
$result  = array(true$offset$token); 
            
            return array(
true$offset$token); // Treffer zurückliefern
        
} catch (Exception $e) {
            throw new 
Exception("match with regular expression failed"0$e);
        }
    }
}
?>
Als Ergebnis bekomme ich relativ fix eine halbwegs brauchbare Ausgabe:
Code:
CREATE TABLE `benchmark` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(255) COLLATE utf8_bin NOT NULL,
  `description` text COLLATE utf8_bin NOT NULL,
  `last_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `created` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
Code:
DEBUG ARG 1:

array(104) {
  [0]=>
  object(Anti_StringParser_Token)#52 (2) {
    ["_name"]=>
    string(10) "whitespace"
    ["_value"]=>
    string(29) "        
        
        		
"
  }
  [1]=>
  object(Anti_StringParser_Token)#53 (2) {
    ["_name"]=>
    string(7) "keyword"
    ["_value"]=>
    string(6) "CREATE"
  }
  [2]=>
  object(Anti_StringParser_Token)#54 (2) {
    ["_name"]=>
    string(10) "whitespace"
    ["_value"]=>
    string(1) " "
  }
  [3]=>
  object(Anti_StringParser_Token)#55 (2) {
    ["_name"]=>
    string(7) "keyword"
    ["_value"]=>
    string(5) "TABLE"
  }
  [4]=>
  object(Anti_StringParser_Token)#56 (2) {
    ["_name"]=>
    string(10) "whitespace"
    ["_value"]=>
    string(1) " "
  }
  [5]=>
  object(Anti_StringParser_Token)#57 (2) {
    ["_name"]=>
    string(10) "identifier"
    ["_value"]=>
    string(11) "`benchmark`"
  }
  [6]=>
  object(Anti_StringParser_Token)#58 (2) {
    ["_name"]=>
    string(10) "whitespace"
    ["_value"]=>
    string(1) " "
  }
  [7]=>
  object(Anti_StringParser_Token)#59 (2) {
    ["_name"]=>
    string(1) "("
    ["_value"]=>
    string(1) "("
  }
(naja usw.).

Leider nur halbwegs brauchbar. Siehe object#57. Schön und gut, dass der Identifier erkannt wurde, allerdings wären es ja richtiger Token "`" + Token String "benchmark" + Token "`".

Ich denke hier habe ich das Vorgehen von einem Scanner (oder heißt es Lexer?) nicht verstanden. Er muss ja, sobald er ein "`" findet auf einen anderen Modus schalten (lies alles ein, (fast) egal was kommt bis zum nächsten "`"). Ursprünglich dachte ich, dass ein Lexer erstmal alles ganz dumm zerlegt und erst der Parser dann sagt, he da ist ein Token "`" das niemals geschlossen wird. Wenn das der Fall wäre, könnte mein Lexer ja aber nicht wissen, dass nach dem öffnenden "`" nur stupide alles bis zum nächsten "`" einlesen muss. Hier ist mein Lexer offenbar noch zu blöd für, also vermutlich falsch konzipiert.

Im Internet konnte ich leider keine gute Anleitung für den Lexer/Parserbau finden. Der Wikiartikel dazu ist auch sehr knapp.

Oder bin ich jetz schon auf einem guten Weg und muss nurnoch die "identifier" oder "string" Tokens intern auf mehrere verteilen (Aggregation)?
__________________
"Nuschel ich?" - "Was?"

Geändert von Chriz (07.01.2011 um 21:29 Uhr). Grund: Quellcodekommentare
Chriz ist offline   Mit Zitat antworten
Sponsor Mitteilung
PHP Code Flüsterer

Registriert seit: 21.08.2005
Beiträge: 4682
PHP-Kenntnisse:
Fortgeschritten

Alt 07.01.2011, 22:13  
Moderator
 
Benutzerbild von Chriz
 
Registriert seit: 11.05.2008
Beiträge: 6.269
Chriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer Anblick
Standard

Scheisse mit der Aggregation, Eventhandler, ArrayAccess-Interface und Closures klappts nahezu perfekt:
PHP-Code:
        $this->_addIdentifier('regexp''ml-comment'"\/\*.+\*\/")
             ->
addModifier('s')
             ->
bind('onSuccess', function($success$offset$token) {
                
$aggregate   = new Anti_StringParser_Token_Aggregate();
                
$aggregate[] = new Anti_StringParser_Token('ml-comment-open''/*');
                
$aggregate[] = new Anti_StringParser_Token('comment'mb_substr($token->getValue(), 2, -2));
                
$aggregate[] = new Anti_StringParser_Token('ml-comment-close''*/');
                return array(
$success$offset$aggregate);
            }); 
Macht haargenau was es soll. Falls euch trotzdem was am Konzept nicht gefällt oder ihr es besser wisst, nur her damit!
Code:
  object(Anti_StringParser_Token_Aggregate)#67 (1) {
    ["_elements":protected]=>
    array(3) {
      [0]=>
      object(Anti_StringParser_Token)#68 (2) {
        ["_name":protected]=>
        string(15) "ml-comment-open"
        ["_value":protected]=>
        string(2) "/*"
      }
      [1]=>
      object(Anti_StringParser_Token)#69 (2) {
        ["_name":protected]=>
        string(7) "comment"
        ["_value":protected]=>
        string(31) " scho



-- queeeek
wieder oins"
      }
      [2]=>
      object(Anti_StringParser_Token)#70 (2) {
        ["_name":protected]=>
        string(16) "ml-comment-close"
        ["_value":protected]=>
        string(2) "*/"
      }
    }
  }
Evtl. noch Fliegengewichtspattern einbauen xD



Edit:

So, nach dem ich gemerkt hab wie rüpelhaft man in PHP mit Strings umgehen kann hab ich das ganze umgemodelt auf Streams, ist jetzt zwar immer noch lahm wie ne Krücke, aber kann immerhin 500 KB mal unterm Timeout/Memory Limit verarbeiten. Klingt für mich aber immer noch voll wenig, weiß aber nicht wo ich noch an der Performance schrauben könnte. Prüfe bereits Identifier auf maximal 256 Zeichen, wie im Handbuch von MySQL erläutert, bei Strings gehe ich momentan von 2^16 Bytes aus (entspricht TEXT), lässt sich aber anpassen. Das lustige ist, ich hatte bei fets() anfangs als $length Parameter PHP_INT_MAX genommen, weil mir strlen(stream_get_contents()) für die Restlänge doch etwas langsam vorkam. Wurde sehr schnell eines besseren belehrt, als PHP 2 GB Speicher allokieren wollte

Das komische ist, dass jetzt feof() nicht mehr funktioniert und ich einen (von mir geworfenen) Syntaxerror bekomme, wenn das Dateiende erreicht ist. Die Position (per ftell() überprüft) entspricht jetzt genau dem Dateiende, auch wenn Notepad++ und Windows das anders sehen. In Eclipse wird scheinbar die wahre Bytegröße (über Properties) ermittelt und das ist genau der Wert. Die Hinweise im feof()-Handbuch treffen auch meinen Fall nicht zu, habe zumindest den Server durchgestartet. Der erste Usernote hat allerdings funktioniert, allerdings etwas schade, weil das so vermutlich etwas langsamer ist als feof() selbst. Schaffe jetzt etwa 5500 Tokens pro Sekunde zu erzeugen, wobei ich die Tokens in Arrays umwandel, ich glaub bei ein paar tausend Objekten geht PHP dann doch in die Knie. Fliegengewichtspattern ist also Pflicht.
__________________
"Nuschel ich?" - "Was?"
Chriz ist offline   Mit Zitat antworten
Alt 08.01.2011, 09:46  
Moderator
 
Benutzerbild von robo47
 
Registriert seit: 03.09.2004
Beiträge: 11.792
PHP-Kenntnisse:
Fortgeschritten
robo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz sein
Standard

Gibt es den Code irgendwo in form eines repositories oder so dass man mal damit spielen/testen kann ?

Hast du dir mal den uralten PEAR-SQL-Parser oder die PEAR-Systeme zum Thema Parsen/Lexen angeschaut/evaluiert ?
robo47 ist offline   Mit Zitat antworten
Alt 08.01.2011, 13:16  
Moderator
 
Benutzerbild von Chriz
 
Registriert seit: 11.05.2008
Beiträge: 6.269
Chriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer Anblick
Standard

Nein, PEAR ist mir dabei noch nicht über den Weg gelaufen, lohnt es sich den mal anzuschauen? Hab ihn mir mal in die Bookmarks geschoben und schau heut nachmittag drauf.

Hier ist mal der aktuelle Stand von DB-Reader fürs spätere ORM und der Scanner/Parser. Habs so hingebogen dass dus grad irgendwo ins localhost kopieren kannst. Wenn du Zeit findest wärs toll wenn du nen Blick riskieren könntest, vor allem soviel Code ist es garnicht, eigentlich nur Anti_StringParser_Myql für die Definition der Tokenmatcher und Anti_StringParser::_scan() für das Auslesen (25 Zeilen).
Angehängte Dateien
Dateityp: zip test123.zip (28,8 KB, 16x aufgerufen)
__________________
"Nuschel ich?" - "Was?"
Chriz ist offline   Mit Zitat antworten
Alt 08.01.2011, 16:17  
Moderator
 
Benutzerbild von robo47
 
Registriert seit: 03.09.2004
Beiträge: 11.792
PHP-Kenntnisse:
Fortgeschritten
robo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz sein
Standard

Ob sich PEAR lohnt kann ich dir nicht sagen, hab ich noch nicht genutzt, nur weis ich dass es da was in der richtung gibt

btw entwickelst du sowas übern browser und ohne tests ?
robo47 ist offline   Mit Zitat antworten
Alt 08.01.2011, 17:27  
Moderator
 
Benutzerbild von Chriz
 
Registriert seit: 11.05.2008
Beiträge: 6.269
Chriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer Anblick
Standard

Äh ja, ich bin da ganz oldschool. Ist ja wie gesagt nur für mich, weil ich meistens nach dem selben Schema arbeite. Datenbank erstellen, abstrakte Data-Models generieren (mit setter/getter) und dann muss ich nicht mehr soviel tippen. Erspart unglaublich viel Arbeit. Nächster Schritt wäre dann, dass ich mir auch die Zugriffe auf die DB generieren lasse (CRUD). Die Information ist ja da und ich muss wieder etwas weniger tippen. Hast du denn Erfahrung mit dem Parserbau? Zufrieden wäre ich ja, wenn man damit auch einen BBCode-Parser oder ähnliches konfigurieren könnte. Nur wie es mit Freitexten aussieht weiß ich gerade noch nicht. Ich glaub ich muss einfach nochmal die richtige Lektüre finden.
__________________
"Nuschel ich?" - "Was?"
Chriz ist offline   Mit Zitat antworten
Alt 08.01.2011, 20:04  
Moderator
 
Benutzerbild von robo47
 
Registriert seit: 03.09.2004
Beiträge: 11.792
PHP-Kenntnisse:
Fortgeschritten
robo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz seinrobo47 kann auf vieles stolz sein
Standard

oldschool ?

Nein mit Parserbau hab ich leider keine Erfahrung, wollte mir das nur mal am anschauen weil es mich ein bißchen interessiert hat.

Langsam würde ich es auch nicht unbedingt bezeichnen, ist ja auch zumindest für klassengeneration nichts was sooooo relevant wäre, bei crud und ddl-query-parsing dann wohl schon eher.

bei mir braucht er für die scanner.php 0.03s:

php -f scanner.php > /dev/null
0.03s user 0.02s system 97% cpu 0.053 total

für die db-reader 0.34 (13 datenbanken mit insgesamt ~ 190 tabellen)
php -f db-reader.php > /dev/null
0.34s user 0.10s system 90% cpu 0.482 total


Was das auslesen von Datenbankstrukturen angeht fällt mir noch Doctrine1 ein, das kann einem models auch aus einer db erstellen, daraus yaml erzeugen und jeweils in die andere richtung und das für verschiedene DB-systeme, vielleicht bringt dich ein blick in die srouces weiter wie die das alles machen weil ne DDL haben die ja auch für queries.
robo47 ist offline   Mit Zitat antworten
Alt 09.01.2011, 00:50  
Moderator
 
Benutzerbild von Chriz
 
Registriert seit: 11.05.2008
Beiträge: 6.269
Chriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer Anblick
Standard

Hi,

also meine CRUD-Klassen sollen dann selbst den Parser nicht benutzen, eher umgekehrt soll der Parser dann benutzt werden um die (abstrakten) Klassen einmalig zu generieren. Wenn sich was ändert brauch ich dann nur das Skript anwerfen und sie werden erneut erzeugt.

Der Parser klappt übrigens unglaublich gut, baue grade den Parse-Tree für CREATE TABLE-Statements auf, eigentlich wie die formale Sprache hier:
http://dev.mysql.com/doc/refman/5.1/...ate-table.html

Also mit Platzhalter (ich benutz Events), um z.B. für "tbl_name" einen generierten Parse-Tree zu erzeugen. Klappt sogar so gut, dass er genauer ist als ich. TEMPORARY ist kein Keyword! Bis ich das endlich gemerkt hab ... Hier mal ein kurzer Ausschnitt (wens interessiert):

PHP-Code:
<?php
        $this
->_bind("onParse", function($tokens) use ($p$b) {
            return 
$p->parseTokenTree(
                
$tokens,
                
$b->listing// eine Abfolge von Befehlen
                    
$b->keyword("CREATE"), // muss das Keyword CREATE treffen
                    
$b->optional(
                        
$b->token("T_STRING""TEMPORARY"// optional der String (!) TEMPORARY
                    
),
                    
$b->keyword("TABLE"),
                    
$b->optional(
                        
$b->keywords("IF""NOT""EXISTS")
                    ),
                    
$b->trigger('tbl_name'), // triggeret einen Event an, siehe weiter unten
                    
$b->optional(
                        
$b->listing(
                            
$b->token("T_BRACKET_ROUND_OPEN"), // runde Klammer erwartet
                            
$b->trigger("create_definition"),
                            
$b->loop// komma-separierte Liste simulieren
                                
$b->token("T_COMMA"),
                                
$b->trigger("create_definition")
                            ),
                            
$b->token("T_BRACKET_ROUND_CLOSE")
                        )
                    ),
                    
$b->optional(
                        
$b->trigger("table_options")
                    ),
                    
$b->optional(
                        
$b->trigger("select_statement")
                    )
                )
            );
        });
        
$this->_bind("tbl_name", function() use ($b) {
            return 
$b->listing(
                
$b->trigger("identifier"), // table name allein, oder als db-name
                
$b->optional(
                    
$b->listing(
                        
$b->token("T_DOT"),
                        
$b->trigger("identifier"// oder dann hier erst table name
                    
)
                )
            );
        });
        
$this->_bind("identifier", function() use ($b) {
            return 
$b->combineOr(
                
$b->listing(
                    
$b->token("T_QUOTE_BACKTICK"),
                    
$b->token("T_STRING"),
                    
$b->token("T_QUOTE_BACKTICK")
                ),
                
$b->token("T_STRING")
           );
        });
?>
Erzeugt einen riesigen Array mit Befehlen, den mein Parser dann durchgeht:
PHP-Code:
<?php
    
public function parseTokenTree(array &$tokens, array $tree$optional false)
    {
        
$token current($tokens);
        
$key   key($tree);
        
$value current($tree);
        
        switch (
$key) {
            case 
"listing":
                
$success true;
                foreach (
$value as $node) {
                    
$success  $this->parseTokenTree($tokens$node$optional);
                    if (
$optional && !$success) {
                        return 
true;
                    }
                    
                    
$success  $success || $optional;
                    
$optional $optional && !$success;
                }
                return 
$success;
            case 
"token":
                
$match $value === $token || ($value[1] === null && $token[0] == $value[0]);
                if (
$match) {
                    echo 
"matching token: " $token[1] . " ";
                    
next($tokens);
                    return 
true;
                }
                if (
$optional) {
                    return 
null;
                }
                
Debug::stop($token$key$value);
                throw new 
Exception("unexpected token #1: " $this->_getTokenName($token[0]) . "(" $token[1] . ")");
            case 
"optional":
                return 
$this->parseTokenTree($tokens$valuetrue);
            case 
"trigger":
                return 
$this->parseTokenTree($tokens$this->_trigger($value), $optional);
            case 
"or":
                foreach (
$value as $node) {
                    if (
$this->parseTokenTree($tokens$nodetrue)) {
                        return 
true;
                    }
                }
                throw new 
Exception("unexpected token #2: " $this->_getTokenName($token[0]) . "(" $token[1] . ")");
            default:
                
Debug::stop($token$key$value);
        }
    }
?>
Wird noch etwas entschlakt, "listing" usw. wird durch numerische Konstanten ersetzt usw., aber anders kann ich den Array sonst manuell nicht mehr lesen.

Danke übrigens fürs Testen, die Zeit klingt gut für mich, reicht wie du schon gesagt hast für Einmal-Vorgänge völlig aus.
__________________
"Nuschel ich?" - "Was?"
Chriz ist offline   Mit Zitat antworten
Alt 11.01.2011, 14:39  
Erfahrener Benutzer
 
Registriert seit: 02.09.2009
Beiträge: 1.020
PHP-Kenntnisse:
Fortgeschritten
mquadrat befindet sich auf einem aufstrebenden Ast
Standard

Warum gehst du denn für das Generieren der Modelle über die DDL? Wenn du das Modell aus einer existierenden Tabelle generieren willst (backwards engineering) dann kann man dem DBMS doch bestimmt per Query die Definition entlocken?!

Oder versteh ich gerade deine Intention falsch?
__________________
Wir suchen PHP Entwickler (Vollzeit) im Raum Darmstadt / Rhein-Main. Infos via E-Mail mueller@new-frontiers.de
mquadrat ist offline   Mit Zitat antworten
Alt 11.01.2011, 16:26  
Moderator
 
Benutzerbild von Chriz
 
Registriert seit: 11.05.2008
Beiträge: 6.269
Chriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer AnblickChriz ist ein wunderbarer Anblick
Standard

Soweit ich weiß bekomme ich anders nicht die Fremdschlüsselbeziehungen. Außerdem kann es sein, dass die Tabelle eben nur als DDL vorliegt und nicht als existierende DB.
__________________
"Nuschel ich?" - "Was?"
Chriz ist offline   Mit Zitat antworten
Antwort


Themen-Optionen
Thema bewerten
Thema bewerten:

Forumregeln
Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are an
Gehe zu

Besucher kamen über folgende Suchanfragen bei Google auf diese Seite
php lexer, parser bauen, lexer php, php parser bauen, lexer parser php, string parser, php lexer parser, simple php lexer parser, lexer parser tutorial, sql unexpected token \collate\, delphi parser lexer scanner token, javascript unexpected value token, regex for ddl, gewichten von token ergebnisse parser, html lexer php, html parsebaum php, tutorial lexer deutsch, statement lexer php, parser bauen php, sql parser bauen

Alle Zeitangaben in WEZ +2. Es ist jetzt 01:31 Uhr.




Powered by vBulletin® Version 3.7.2 (Deutsch)
Copyright ©2000 - 2012, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.2.0
Aprilia-Forum, Aquaristik-Forum, Liebeskummer-Forum, Zierfisch-Forum, Geizkragen-Forum