2014-11-24 3 views
5

Ich implementiere ein neues DSL in Marpa und (von Regexp :: Grammars kommend) bin ich mehr als zufrieden. Meine Sprache unterstützt eine Reihe von unären und binären Operatoren, Objekte mit C-style Bezeichnern und Methodenaufrufe unter Verwendung der bekannten Punktnotation. Zum Beispiel:Marpa: Kann ich Schlüsselwörter explizit als Bezeichner verbieten?

foo.has(bar == 42 AND baz == 23)

fand ich die prioritized rules Funktion von Sprache Marpas Grammatikbeschreibung angeboten und haben sich auf diesem viel verlassen, so dass ich fast nur eine G1 Regel Expression. Auszug (viele Alternativen, und semantischen Aktionen der Kürze halber weggelassen):

Expression ::= 
     NumLiteral 
    | '(' Expression ')'    assoc => group 
    || Expression ('.') Identifier 
    || Expression ('.') Identifier Args 
    | Expression ('==') Expression 
    || Expression ('AND') Expression 

Args  ::= ('(') ArgsList (')') 
ArgsList ::= Expression+    separator => [,] 

Identifier   ~ IdentifierHeadChar IdentifierBody 
IdentifierBody  ~ IdentifierBodyChar* 
IdentifierHeadChar ~ [a-zA-Z_] 
IdentifierBodyChar ~ [a-zA-Z0-9_] 

NumLiteral ~ [0-9]+ 

Wie Sie sehen, ich bin mit der Scanless Schnittstelle (SLIF). Mein Problem ist, dass diese parsen auch, zum Beispiel:

foo.AND(5) 

Marpa weiß, dass es nur eine Kennung nach einem Punkt sein kann, so dass er nicht die Ansicht, auch die Tatsache, dass AND könnte ein Schlüsselwort sein. Ich weiß, dass ich dieses Problem vermeiden kann, indem ich eine separate Lexing-Stufe mache, die AND explizit als Schlüsselwort identifiziert, aber dieser kleine Papierschnitt ist die Mühe nicht wirklich wert.

Gibt es eine Möglichkeit in SLIF, die Identifier-Regel auf Nicht-Schlüsselwort-Bezeichner zu beschränken?

+0

Was meinen Sie mit "Schlüsselwort"? 'assoc' und' separator' sind Schlüsselwörter im Marpa-Jargon. – choroba

+0

@choroba, Er meint, wenn er einen Operator 'AND' definiert, will er nicht, dass er als Bezeichner erlaubt ist. – ikegami

+0

Ich habe das nicht getestet, aber vielleicht möchten Sie sich das 'latm' Adverb ansehen. Dies ermöglicht es Ihnen, Marpas Wissen darüber, was Lexem akzeptabel ist, auf einer Lexem-Basis auszuschalten - was es in der Tat "dumm" für das eine Lexem macht, so dass es denkt, dass ein "UND" OK ist, und dann scheitern Sie die Analyse, wie Sie wollen. Koppeln Sie dies vielleicht mit einer höheren Lexempriorität, so dass 'AND' als Operator gegenüber 'AND' als Bezeichner bevorzugt wird. Aus der Spitze meines Kopfes, aber ich hoffe, es hilft. –

Antwort

2

Ich weiß nicht, wie man so etwas in der Grammatik ausdrücken kann. Sie können einen Zwischen nicht-Terminal für Identifier einführen, die den Zustand überprüfen würde, aber:

#!/usr/bin/perl 
use warnings; 
use strict; 
use Syntax::Construct qw{ // }; 

use Marpa::R2; 

my %reserved = map { $_ => 1 } qw(AND); 

my $grammar = 'Marpa::R2::Scanless::G'->new(
    { bless_package => 'main', 
     source => \(<< '__GRAMMAR__'), 

:default ::= action => store 

:start ::= S 
S ::= Id 
    | Id NumLiteral 
Id ::= Identifier action => allowed 

Identifier   ~ IdentifierHeadChar IdentifierBody 
IdentifierBody  ~ IdentifierBodyChar* 
IdentifierHeadChar ~ [a-zA-Z_] 
IdentifierBodyChar ~ [a-zA-Z0-9_] 

NumLiteral ~ [0-9]+ 

:discard ~ whitespace 
whitespace ~ [\s]+ 

__GRAMMAR__ 
    }); 

for my $value ('ABC', 'ABC 42', 'AND 1') { 
    my $value = $grammar->parse(\$value, 'main'); 
    print $$value, "\n"; 
} 


sub store { 
    my (undef, $id, $arg) = @_; 
    $arg //= 'null'; 
    return "$id $arg"; 
} 

sub allowed { 
    my (undef, $id) = @_; 
    die "Reserved keyword $id" if $reserved{$id}; 
    return $id 
} 
+0

oops, doh! [wird selbstzerstören] – ikegami

+0

Ich habe verschiedene Permutationen der 'priority' und' latm' Lexeme Adverbien ausprobiert, aber das ist das Einzige was meinen Unit Test tatsächlich grün gemacht hat. Die Fehlermeldungen sind möglicherweise nicht so hübsch, aber zumindest akzeptiert sie die Grammatik korrekt. –

+0

@StefanMajewsky: Eine "negative Regel" wäre schön zu haben. Danke für eine interessante Frage. – choroba

3

Sie Lexems Prioritäten nur für eine solche Art der Sache bestimmt verwenden können, ist das Beispiel here in Marpa :: R2-Testsuite.

Grundsätzlich deklarieren Sie <AND keyword> ~ 'AND' lexeme und geben Sie ihm Priorität 1, so dass es gegenüber Identifier bevorzugt ist. Das muss es tun.

P.S. Ich habe das obige Skript leicht modifiziert, um ein Beispiel zu geben - code, output.

+1

Wenn Sie die Grammatik mit 'S :: = Id Call erweitern; Ruf :: = '.' Id '(' ')' 'und fügen Sie dann einen Testfall' ABC.AND() 'hinzu. Sie werden sehen, dass _exactly_ das unerwünschte Verhalten akzeptiert wird. Ich versuche auch, dies zu verhindern! Irgendwelche Ideen? Können Aktionen verwendet werden, um ein Token basierend auf seiner Form abzulehnen? –

+0

Aktionen können verwendet werden, aber das ist nicht besonders effizient - Aktionen werden in der Evaluierungsphase aufgerufen, wenn die Eingabe gelesen wird. Ereignisse sind besser, sieh dieses Gist - https://gist.github.com/rns/d19b40ffc5523659dec9 - 'AND' Bezeichner wird zurückgewiesen, sobald es in der Eingabe getroffen wird. – rns

+0

Ahh nett. Ich sehe jetzt den Unterschied zwischen Aktionen und Ereignissen deutlicher. Ereignisse scheinen ein guter Ort zu sein, um auf obligatorische Whitespaces zu testen - eine Frage, die ich gesehen habe, wurde ein paar Mal ohne eine eindeutige Antwort gestellt. Zum Beispiel, wenn Ihre Grammatik eine Liste von Zahlen erlaubt, wäre es "nett", eine Warnung zu geben, die nach einer Trennung zwischen der '4' und dem '-' in '12 34-56 78' fragt. Ist das sicher zu sagen? '$ r-> literal ($ lexeme_start + $ lexeme_length, 1);' ist das nächste Zeichen nach dem erkannten Token? Auch das "Abfallen" des Endes muss behandelt werden ... –

Verwandte Themen