Pegjs: Andere Grammatiken importieren/einschließen

Erstellt am 16. Aug. 2011  ·  32Kommentare  ·  Quelle: pegjs/pegjs

Es könnte äußerst nützlich sein, die Möglichkeit zu haben, Grammatiken zu definieren, indem Regeln aus anderen Grammatiken importiert werden.

Mehrere Ideen;

<strong i="7">@include</strong> "expression.pegjs"
(or <strong i="8">@from</strong> "expression.pegjs" import expression)

tag_if
    = "if" space? expression space? { ... }

<strong i="9">@import</strong> "expression.pegjs" as expr

tag_if
    = "if" space? expr.expression space?

Im Idealfall würde dies nicht den gesamten Code in jeder .pegjs neu generieren, die eine andere ; vielleicht müssten wir das Verhalten von parse() etwas in der Art ändern;

Bearbeiten gemäß dem, was Sie in der Ausgabe options gesagt haben;

parse(input, startRule)
->
parse(input, { startRule: "...", startPos : 9000 })

Und am Ende, wenn startPos != 0 && result !== null , überprüfen wir nicht, ob wir bis input.length gegangen sind, sondern geben stattdessen das Ergebnis sowie die endPos zurück (weiß nicht wirklich, wie man das elegant macht - vielleicht einfach den Optionsparameter ändern ?).

Es würde die Wiederverwendbarkeit von Grammatiken und die Modularisierung des Codes ermöglichen, was meiner Meinung nach zwei äußerst wichtige Aspekte des Codierens im Allgemeinen sind.

feature

Hilfreichster Kommentar

@Dignifiedquire Ich denke gerade über Syntax & Semantik nach, die sich wahrscheinlich am besten an einem Beispiel erklären lässt:

static-languages.pegjs

langauges  = "C" / "C++" / "Java" / "C#"

dynamic-languages.pegjs

languages = "Ruby" / "Python" / "JavaScript"

all-languages.pegjs

static  = require("./static-languages")
dynamic = require("./dynamic-languages")

all = static.languages / dynamic.languages

Jede .pegjs Datei würde implizit ein Modul definieren, das alle darin enthaltenen Regeln exportiert. Das Konstrukt <name> = require(<module>) würde ein solches Modul importieren. Seine Regeln wären dann in einem Namespace verfügbar.

Dieses Design ist Node.js bewusst ähnlich. Die Verwendung von Namespaces vermeidet Konflikte. Ich sehe zwei Nachteile:

  1. Das Konstrukt <name> = require(<module>) ist Regeldefinitionen zu ähnlich und kann daher verwirrend sein (man könnte meinen, dass nur eine Regel importiert wird).
  2. Die Syntax von . Konflikt mit der aktuellen Bedeutung von . , die „beliebiges Zeichen“ ist. Dies kann durch hässliche Hacks gelöst werden (zB . umgeben von Whitespace bedeutet „beliebiges Zeichen“, während . umgeben von Bezeichnern einen Namensraumnamen von einem Regelnamen trennt) oder durch Ändern der Syntax (zB Verwenden des Schlüsselworts any , um „beliebiges Zeichen“ darzustellen).

Alle 32 Kommentare

Ich stimme zu, dass dies ein wichtiges Feature ist, ich möchte dies nach Version 1.0 tun.

(Übrigens gefällt mir die von Ihnen vorgeschlagene Python-ähnliche Syntax nicht – etwas Ähnliches wie require von Node.js wäre besser, weil es JavaScript-Programmierern vertrauter wäre. Aber das ist eine Kleinigkeit, die gebügelt werden kann später heraus.)

Würden Sie es für eine Aufnahme vor 1.0 in Betracht ziehen, wenn es mit einem Patch bereitgestellt wird?

Ich stimme Ihrer Bemerkung zur Python-Syntax zu.

+1 für diese Funktion

@ceymard Ja, ich würde es in Betracht ziehen.

+1 für die Funktion und +1 für die Aufnahme des require Stils

@dmajda @ceymard Haben Sie schon

@Dignifiedquire Ich denke gerade über Syntax & Semantik nach, die sich wahrscheinlich am besten an einem Beispiel erklären lässt:

static-languages.pegjs

langauges  = "C" / "C++" / "Java" / "C#"

dynamic-languages.pegjs

languages = "Ruby" / "Python" / "JavaScript"

all-languages.pegjs

static  = require("./static-languages")
dynamic = require("./dynamic-languages")

all = static.languages / dynamic.languages

Jede .pegjs Datei würde implizit ein Modul definieren, das alle darin enthaltenen Regeln exportiert. Das Konstrukt <name> = require(<module>) würde ein solches Modul importieren. Seine Regeln wären dann in einem Namespace verfügbar.

Dieses Design ist Node.js bewusst ähnlich. Die Verwendung von Namespaces vermeidet Konflikte. Ich sehe zwei Nachteile:

  1. Das Konstrukt <name> = require(<module>) ist Regeldefinitionen zu ähnlich und kann daher verwirrend sein (man könnte meinen, dass nur eine Regel importiert wird).
  2. Die Syntax von . Konflikt mit der aktuellen Bedeutung von . , die „beliebiges Zeichen“ ist. Dies kann durch hässliche Hacks gelöst werden (zB . umgeben von Whitespace bedeutet „beliebiges Zeichen“, während . umgeben von Bezeichnern einen Namensraumnamen von einem Regelnamen trennt) oder durch Ändern der Syntax (zB Verwenden des Schlüsselworts any , um „beliebiges Zeichen“ darzustellen).

@dmajda Da das Muster <identifier> = <expression> bereits von den Regeldefinitionen verwendet wird, warum nicht so etwas tun:

static := require("./static-languages")
dynamic := require("./dynamic-languages")

all = static::languages / dynamic::languages

Das :: wird nirgendwo in PEG.js verwendet, das mir bekannt ist, und macht es einfach, zwischen Namespaces und anderen Dingen zu unterscheiden. Ich bin mir nicht sicher über die := es auf den Punkt bringt, aber es fühlt sich für Javascript sehr fremd an.

Auch wenn Sie Namespaces verwenden möchten, sollten Sie denken, dass es nur einen Namespace pro Datei geben sollte oder sollte es eine Möglichkeit geben, mehrere Namespaces in einer Datei wie folgt zu erstellen:

static := {
  languages  = "C" / "C++" / "Java" / "C#"
}

dynamic := {
  languages = "Ruby" / "Python" / "JavaScript"
}

Ich bin kein großer Fan von :: und := , sie sehen in der Javascript/CoffeeScript-Welt fremd aus.

Ich möchte auch die Dinge einfach halten und Namespaces implizit nur durch die Anforderung von Dateien definieren. Ich sehe keinen großen Bedarf für etwas Komplizierteres.

Wie wäre es einfach:

<strong i="6">@require</strong> foo = "./foo"

bar = foo:languages

Doppelpunkte sind ein Kompromiss, werden jedoch an vielen Stellen verwendet, um Namespaces zu trennen: C++, C#, XML usw.

: wird für viele, viele funktionale Programmierer immer mit cons . Ich schlage vor, sich von diesem Betreiber fernzuhalten. :: sieht für mich gut aus. Wird das nicht für C++-Namespaces verwendet? Ich bin auch noch nicht überzeugt, dass . eine schlechte Wahl ist.

. kann nicht ohne Breaking Change verwendet werden. Es wäre mehrdeutig in der Sprache.

:: wird in C++ für Namespaces und in C# für Namespace-Präfixe verwendet ( zum Beispiel global::System ).

Ich dachte an eine schnelle Problemumgehung zu diesem Thema - um nur die einfache Vererbung zu lösen - Pegjs-Dateien zusammenzukleben, während alles mit Namensräumen versehen ist.

Dies könnte Grammatiken zu ausführlich machen und erfordert einen Aufbauschritt – aber wenn man die positive Seite betrachtet, würde es Sie zwingen, granulare DRY&OTW-Grammatiken zu verwenden

Und was das Markup betrifft, ohne zu sagen, dass dies zu diesem Thread passt, aber nur eine Option, die es zu berücksichtigen gilt, ich habe mich für ein einfaches entschieden__

languages = static__languages / dynamic__languages
<static-languages.pegjs>
<dynamic-languages.pegjs>
/* alternative */
languages = STATIC__languages / DYNAMIC__languages

@andreineculau Ich mache dies im Grunde bereits mit einem Build-Schritt. Wenn Sie und andere also nur nach etwas suchen, um nützliche Parser aus einer Grammatik mit einem Abhängigkeitsbaum zu generieren (wo ein einzelner Parser generiert wird, der die kombinierte Grammatik implementiert), könnte ich Bereinigen Sie, was ich habe, und geben Sie es frei, damit sich die Diskussion neu darauf konzentrieren kann, wie man damit dauerhafter umgeht.

Eine andere Sache: Wenn man sich diesem in erster Linie durch das Entwerfen von Erweiterungen der Grammatiksyntax annähert, wird etwas Wichtiges übersehen, nämlich dass einer der Hauptgründe, warum wir alle den Drang haben, Regeln aus anderen Grammatiken zu übernehmen (ein weiterer Grund ist die Klarheit), die Notwendigkeit, Parser zu schreiben, die teilen viel Logik. Auch wenn generierte Parser zum Zeitpunkt des Parsens möglicherweise nie sinnvoll neu zusammengesetzt werden können, scheint es wichtig zu sein, dass ein Grammatikbaum einen Parserbaum erzeugt und nicht einen monolithischen Parser. Es ist am wichtigsten, wenn eine Reihe von Parsern Teil einer Web-Benutzeroberfläche ist, aber es schadet im Allgemeinen nicht, unnötiges Aufblähen im generierten Code zu vermeiden.

@odonell +1 für das Freigeben von allem - egal ob du die Zeit zum Aufräumen hast

und +1 für die Klarstellung. Dies sollte als schnelle Problemumgehung betrachtet werden, nicht als langfristige richtige Lösung.

@odonnell meine https://github.com/andreinecula/core-pegjs - bitte stupse mich an, wenn du etwas Besseres hast.

+1 für diese Funktion

:+1:

:+1:

:+1:

Ich habe ein Plugin/eine Erweiterung für PEG.js geschrieben, das Importe durchführt: https://github.com/casetext/pegjs-import.

Auch hierfür +1.

Ich implementiere dies in #308 auf generische Weise: Die Einbeziehung von Grammatik ist nur eine Möglichkeit, Zerlegungsregeln zu implementieren.

Tolle Funktion :+1:

Ich freue mich darauf, es veröffentlicht zu sehen.

:+1:

Genial! :+1:

@dmajda Ich komme zu spät zu dieser Party, aber ich frage mich, wie oft wir viele Regeln aus einer anderen Bibliothek importieren müssen. Ich würde gerne Dinge wie Url und Email in meine komponierten Grammatiken importieren, aber es ist mir egal, dass Url auch Dinge wie HierarchicalPart und AsciiLetter . Glauben Sie, dass so etwas wie die benannten Exporte von Node ein gangbarer Weg wäre, die Vorteile des Namespace beizubehalten, aber direkte benannte Importe zuzulassen?

import { SchemalessUrl, Url } from "./Urls.pegjs"

Token
  = PhoneNumber
  / Url
  / SchemalessUrl

Namespace war ein Problem für mich, während ich versuche, anders komponierbare Grammatiken zu schreiben. Ich stecke gerade fest, Dateien in Dateien einzuschließen und Dinge so zu benennen, wie PHP-Funktionen benannt wurden, bevor sie die richtigen Namespaces einführten: UrlIpHost , HtmlQuotedString usw.

@dmajda @futagoza

Gibt es Fortschritte zu diesem Thema? oder lebt die Hauptdiskussion jetzt auf #473?
Meine Grammatikdatei wächst sehr schnell :(
Es wäre schön, es auf mehrere aufzuteilen

Es würde mir nichts ausmachen, Grammatiken zwischen Dateien aufzuteilen, nur für Organisation und Komposition. Es würde es einfacher machen, sie zu testen und wiederzuverwenden, und vielleicht auch eine Möglichkeit bieten, Grammatiken dynamisch auszutauschen? Nur ein paar Gedanken.

Das JavaScript-Beispiel, das ich als Basis verwendet habe, umfasst über 1.300 Zeilen. Es dauerte eine Weile, um herauszufinden, wo alles war, und herumzuspringen und verschiedene Abschnitte zu bearbeiten.

@mikeaustin Ich sehe diese Funktion als eine Art Node.JS required :

cat bash.pegjs
{
const _ = require("whitespace");
const LB = require("line_break");
const CodeBlock = require("code_block");
const BoolExpr = require("boolean_expression");
}
...
IfStatement = "if" _ "[" BoolExpr "]" _ ";" _ "dann" LB? CodeBlock "fi"

Ich stimme zu, Grammatiken aufzuteilen und modular zu machen ist eine großartige Funktion, aber die Handhabung dieser Fälle wäre ein Problem:
1- Untergrammatik, die auf einer globalen Variablen beruht, die im Hauptgrammatikcode definiert wurde ?
2- Variablen und Grammatikname duplizieren?

IMO, ein zeitlich bequemer Ansatz wäre, ein neues Add-On für PEG.js (unabhängig von PEG.js) zu erstellen, das ein Schlüsselwort zum Importieren definiert (z.
Erstellen Sie eine Reg-Exp- oder eine Peg-Grammatik, um dieses Schlüsselwort zu erkennen, und ersetzen Sie es durch den Inhalt "anotherGrammarFile Location" , und senden Sie den ersetzten Code an PEG.js

Beispiel:

ganze Zahlen.pegjs

integers=[0-9]* {return parseInt(text())}

main.pegjs
arrayOfInteger="["(integers ",")* integers"]"
@load("integers.pegjs")

Beachten Sie, dass bei Verwendung dieser Methode, wenn jemand die Startgrammatik nicht definiert und peg.js die erste Grammatik als Start annimmt (Grammatik für ganze Zahlen).

Ein Ansatz, dies zu handhaben, besteht darin, dieselben Namen von Dateiname und Startgrammatik zu verwenden und das neue Add-On das Startattribut manuell als Dateinamen konfigurieren zu lassen, oder den gesamten Inhalt am Ende der Datei zu ersetzen.

Der Benutzer sollte für jede Vervielfältigung verantwortlich sein .

Ich möchte nur hervorheben, dass es sich bei diesem Problem in erster Linie um eine Optimierungsanforderung handelt, da Zusammensetzbarkeit/Modularität etwas ist, das Sie selbst erreichen können, insbesondere wenn Sie das gesamte Spektrum der Grammatik beherrschen.

Wenn Sie mit einer Grammatik von 1k Zeilen nicht vertraut sind, teilen Sie sie auf und verketten Sie sie nach Belieben, bevor Sie sie in Pegjs pumpen.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen