2009-02-05 10 views
19

Ich versuche, eine Lisp-Grammatik zu bauen. Einfach richtig? Scheinbar nicht.Lisp Grammatik in Yacc

stelle ich diese Eingaben und Fehlermeldungen ...

(1 1) 
23 23 23 
ui ui 

Dies ist die Grammatik ...

%% 
sexpr: atom     {printf("matched sexpr\n");} 
    | list 
    ; 
list: '(' members ')'  {printf("matched list\n");} 
    | '('')'    {printf("matched empty list\n");} 
    ; 
members: sexpr    {printf("members 1\n");} 
    | sexpr members   {printf("members 2\n");} 
    ; 
atom: ID     {printf("ID\n");} 
    | NUM     {printf("NUM\n");} 
    | STR     {printf("STR\n");} 
    ; 
%% 

So nahe, wie ich sagen kann, ich brauche einen einzigen Nicht-Terminal definiert als ein Programm, auf dem der ganze Parse-Baum hängen kann. Aber ich habe es versucht und es schien nicht zu funktionieren.

bearbeiten - das war mein "Top-Terminal" -Ansatz:

program: slist; 

slist: slist sexpr | sexpr; 

Aber es erlaubt Probleme wie:

(1 1 

Edit2: Die FLEX-Code ist ...

%{ 
    #include <stdio.h> 
    #include "a.yacc.tab.h" 
    int linenumber; 
    extern int yylval; 
%} 
%% 
\n       { linenumber++; } 
[0-9]+      { yylval = atoi(yytext); return NUM; } 
\"[^\"\n]*\"    { return STR; } 
[a-zA-Z][a-zA-Z0-9]*  { return ID; } 
. 
%% 

Ein Beispiel für die Überdeckung ...

(1 1 1) 
NUM 
matched sexpr 
NUM 
matched sexpr 
NUM 
matched sexpr 
(1 1 
NUM 
matched sexpr 
NUM 
matched sexpr 

Was ist der Fehler hier?

edit: Der Fehler war im Lexer.

+0

Was wie sieht Ihre Ausgabe aussehen, wenn sie analysiert die (1 1 Ich kann nicht sehen, wie es zu einem Punkt bekommt? das Schließen nicht erwartet). – Bearddo

+1

Könnten Sie bitte auch Ihre Lex/Flex-Datei posten? Vielleicht gibt es einen Fehler. Auch sollten Sie keine Zeichen verwenden (in der Grammatik, wenn Sie einen Lexer verwenden, bin ich nicht wirklich sicher, wie diese miteinander auskommen.) – jpalecek

+0

Was komisch ist, ist, dass selbst die gültige Liste (1 1 1) nicht erscheint eine Übereinstimmung für eine Liste. Ich würde zwei Dinge versuchen, zuerst machen Mitglieder rekursive: Mitglieder: Mitglieder sexpr | sexpr; Zweitens, tauschen Sie die Reihenfolge der Liste in sexpr: list | atom; Sehen, ob das funktioniert. – Bearddo

Antwort

11

Der Fehler liegt wirklich im Lexer. Ihre Klammern enden als letztes "." im Lexer und erscheinen nicht als Klammern im Parser.

hinzufügen Regeln wie

\)  { return RPAREN; } 
\( { return LPAREN; } 

zum Lexer und ändern Sie alle Vorkommen von '(', ')' zu LPAREN und RPAREN jeweils in den Parser. (Außerdem müssen Sie LPAREN und RPAREN definieren, wo Sie Ihre Token-Liste definieren)

Hinweis: Ich bin mir nicht sicher über die Syntax, könnte die Backslashes falsch sein.

4

Sie haben Recht, dass Sie ein Nicht-Terminal definieren müssen. Das würde als eine Reihe von Sexpr definiert werden. Ich bin mir der YACC-Syntax dafür nicht sicher. Ich bin teilweise zu ANTLR für Parser-Generatoren und die Syntax wäre:

program: sexpr* 

Anzeige 0 oder mehr sexpr.

Aktualisierung mit YACC Syntax:

program : /* empty */ 
     | program sexpr 
     ; 

Nicht in YACC, aber vielleicht hilfreich sowieso sein, hier ist eine vollständige Grammatik in ANTLR v3, die für die Fälle, die Sie beschrieben (ausgenommen Strings in der Lexer funktioniert, weil es nicht wichtig ist, für dieses Beispiel verwendet auch Konsolenausgabe C#, weil das ist, was ich mit) getestet:

program: (sexpr)*; 

sexpr: list 
    | atom   {Console.WriteLine("matched sexpr");} 
    ; 

list:  
    '('')'    {Console.WriteLine("matched empty list");} 
    | '(' members ')' {Console.WriteLine("matched list");} 

    ; 

members: (sexpr)+  {Console.WriteLine("members 1");}; 

atom: Id    {Console.WriteLine("ID");} 
    | Num    {Console.WriteLine("NUM");} 
    ; 


Num: ('0' .. '9')+; 
Id: ('a' .. 'z' | 'A' .. 'Z')+; 
Whitespace : (' ' | '\r' '\n' | '\n' | '\t') {Skip();}; 

Dies wird nicht genau arbeiten, wie in YACC ist, weil YACC und LALR Parser erzeugt, während ANTLR eine modifizierte rekursive Abstieg ist. Es gibt ein C/C++ - Ausgabeziel für ANTLR, wenn Sie diesen Weg gehen wollten.

+0

Aktualisiert mit Informationen über meine Top-Level-Terminal overmatching. –

1

Es ist schon lange her, dass ich mit YACC gearbeitet habe, aber Sie brauchen ein Non-Terminal der obersten Ebene. Könntest du etwas genaueres über "probier es" und "es scheint nicht zu funktionieren" sagen? Oder was sind die Fehler?

Ich würde auch vermuten, dass YACC für eine solche Sprache Syntax-Sprache übertrieben sein könnte. Etwas einfacheres (wie rekursiver Abstieg) könnte besser funktionieren.

+0

Aktualisiert. Außerdem habe ich die Flex/Bison-Tools schon früher benutzt, also macht es mir das Leben leichter. –

+0

Ich würde auf jeden Fall mit dem rekursiven Descent Parser für Lispeln zustimmen - oder sogar einen Push-Down-Automaten – Eclipse

+0

Okay, ich bin ratlos, es sollte keine unausgeglichenen Klammern akzeptieren. Ich nehme an, du benutzt Lex/Flex; Wie definiert Lex/Flex ID? Was du hast, sieht gut für mich aus, also würde ich gerne mehr von dem sehen, was du tust. –

11

Die Lisp-Grammatik kann nicht als kontextfreie Grammatik dargestellt werden und yacc kann den gesamten Lisp-Code nicht analysieren. Es ist wegen Lisp Funktionen wie Lese-Auswertung und programmierbaren Leser. Um also nur einen beliebigen Lisp-Code zu lesen, muss ein vollständiges Lispeln ausgeführt werden. Dies ist keine obskure, nicht verwendete Funktion, aber es wird tatsächlich verwendet. Z. B. CL-INTERPOL, CL-SQL.

Wenn das Ziel ist, eine Teilmenge von Lisp zu analysieren, dann ist der Programmtext eine Folge von Sexprs.

+3

Eigentlich hängt das von der Version von Lisp ab. Wenn Sie nicht auf Common Lisp abzielen oder ein Bootstrap versuchen, kann ein CFG ein guter Ausgangspunkt sein. –

2

Benötigen Sie einen Yacc/Bison Parser? Ein "liest eine Teilmenge der Lisp Syntax" Leser ist nicht so schwer in C zu implementieren (starten Sie mit einer read_sexpr Funktion, Versand an eine read_list wenn Sie ein '(' sehen, das wiederum eine Liste von enthaltenen sexprs bis ein ') 'wird gesehen, andernfalls rufen Sie ein read_atom auf, das ein Atom sammelt und es zurückgibt, wenn es keine atomkonstituenten Zeichen mehr lesen kann.

Wenn Sie jedoch Arbitrary Common Lisp lesen möchten, müssen Sie (im schlimmsten Fall) ein Common Lisp implementieren, da CL die Reader-Laufzeit ändern kann (und sogar zwischen verschiedenen Lese- Tabellenlaufzeit unter Programmkontrolle; ziemlich praktisch, wenn Sie Code laden möchten, der in einer anderen Sprache oder einem anderen Dialekt von Lisp geschrieben wurde).

+0

Ich fotografiere nicht für geläufiges Lispeln. Mein Wunsch ist es, dass ich dies irgendwann auf ein Embedded-Gerät laden und Steuerfunktionen im laufenden Betrieb schreiben/neu schreiben/schreiben kann. Ich benutze Flex/Bison, weil ich weiß, wie man sie benutzt. Nicht weil sie per se die beste Option sind. –

1

Ich habe gerade versucht, meine „yacc Lisp Grammatik“ funktioniert:

%start exprs 

exprs: 
    | exprs expr 
    /// if you prefer right recursion : 
    /// | expr exprs 
    ; 

list: 
    '(' exprs ')' 
    ; 

expr: 
    atom 
    | list 
    ; 

atom: 
    IDENTIFIER 
    | CONSTANT 
    | NIL 
    | '+' 
    | '-' 
    | '*' 
    | '^' 
    | '/' 
    ;