Pegjs: Importar / incluir otras gramáticas

Creado en 16 ago. 2011  ·  32Comentarios  ·  Fuente: pegjs/pegjs

Sería extremadamente útil tener la capacidad de definir gramáticas importando reglas de otras gramáticas.

Varias ideas;

<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?

Idealmente, esto no volvería a generar todo el código en cada .pegjs que incluye otro; tal vez tendríamos que modificar un poco el comportamiento de parse () a algo parecido;

Editando según lo que estaba diciendo en la edición options ;

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

Y al final, si startPos != 0 && result !== null , no verificamos si llegamos hasta el input.length , sino que devolvemos el resultado y los endPos (realmente no sé cómo hacerlo con elegancia - ¿Quizás simplemente modificando el parámetro de opciones?).

Permitiría la reutilización de gramáticas y la modularización del código, que creo que son dos aspectos extremadamente importantes de la codificación en general.

feature

Comentario más útil

@Dignifiedquire Actualmente estoy pensando en la sintaxis y la semántica que probablemente se pueda explicar mejor con un ejemplo:

static-languages.pegjs

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

lenguajes-dinámicos.pegjs

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

all-languages.pegjs

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

all = static.languages / dynamic.languages

Cada archivo .pegjs definiría implícitamente un módulo que exportaría todas las reglas que contiene. La construcción <name> = require(<module>) importaría dicho módulo. Entonces, sus reglas estarían disponibles dentro de un espacio de nombres.

Este diseño es deliberadamente similar a Node.js. El uso de espacios de nombres evitará conflictos. Hay dos desventajas que veo:

  1. La construcción <name> = require(<module>) es demasiado similar a las definiciones de reglas y, por lo tanto, puede resultar confusa (uno podría pensar que solo se importa una regla).
  2. La sintaxis . entra en conflicto con el significado actual de . , que es “cualquier carácter”. Esto se puede resolver con trucos feos (por ejemplo, . rodeado por un espacio en blanco significa "cualquier carácter", mientras que . rodeado por identificadores separa un nombre de espacio de nombres de un nombre de regla) o cambiando la sintaxis (por ejemplo, utilizando la palabra clave any para representar "cualquier carácter").

Todos 32 comentarios

Estoy de acuerdo en que esta es una característica importante, quiero hacer esto después de la versión 1.0.

(Por cierto, no me gusta la sintaxis similar a Python que propones; algo similar a require Node.js sería mejor porque sería más familiar para los programadores de JavaScript. Pero esto es algo menor que se puede arreglar más tarde.)

¿Consideraría incluirlo antes de la versión 1.0 si se le proporcionara un parche?

Estoy de acuerdo con su comentario sobre la sintaxis de Python.

+1 para esta función

@ceymard Sí, lo consideraría.

+1 para la función y +1 para require inclusión de estilo

@dmajda @ceymard ¿Tiene alguna idea sobre cómo implementar esto? Necesito esto para un proyecto en el trabajo y trataré de implementarlo. La pregunta es si esto es solo una adición para dividir gramáticas en varios archivos o algo así como herencia, por lo que uno podría heredar todas las reglas, por ejemplo, y luego sobrescribir reglas específicas en la nueva gramática.

@Dignifiedquire Actualmente estoy pensando en la sintaxis y la semántica que probablemente se pueda explicar mejor con un ejemplo:

static-languages.pegjs

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

lenguajes-dinámicos.pegjs

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

all-languages.pegjs

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

all = static.languages / dynamic.languages

Cada archivo .pegjs definiría implícitamente un módulo que exportaría todas las reglas que contiene. La construcción <name> = require(<module>) importaría dicho módulo. Entonces, sus reglas estarían disponibles dentro de un espacio de nombres.

Este diseño es deliberadamente similar a Node.js. El uso de espacios de nombres evitará conflictos. Hay dos desventajas que veo:

  1. La construcción <name> = require(<module>) es demasiado similar a las definiciones de reglas y, por lo tanto, puede resultar confusa (uno podría pensar que solo se importa una regla).
  2. La sintaxis . entra en conflicto con el significado actual de . , que es “cualquier carácter”. Esto se puede resolver con trucos feos (por ejemplo, . rodeado por un espacio en blanco significa "cualquier carácter", mientras que . rodeado por identificadores separa un nombre de espacio de nombres de un nombre de regla) o cambiando la sintaxis (por ejemplo, utilizando la palabra clave any para representar "cualquier carácter").

@dmajda Como el <identifier> = <expression> ya está tomado por las definiciones de reglas, ¿por qué no hacer algo como esto?

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

all = static::languages / dynamic::languages

El :: no se usa en ningún lugar que yo sepa en PEG.js y facilita la distinción entre espacios de nombres y otras cosas. No estoy seguro acerca de los := que trae el punto, pero se siente muy ajeno a Javascript.

Además, si desea utilizar espacios de nombres, ¿cree que debería haber solo un espacio de nombres por archivo o debería haber una forma de crear varios espacios de nombres en un archivo como este?

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

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

No soy muy fan de :: y := , se ven extraños en el mundo de JavaScript / CoffeeScript.

También me gustaría mantener las cosas simples y definir los espacios de nombres implícitamente solo requiriendo archivos. No veo una gran necesidad de nada más complicado.

¿Qué tal simplemente:

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

bar = foo:languages

Los dos puntos son un compromiso, pero se utilizan para separar espacios de nombres en muchos lugares: C ++, C #, XML, etc.

: siempre estará asociado con cons para muchos, muchos programadores funcionales. Sugiero mantenerse alejado de ese operador. :: parece bien. ¿No se usa para los espacios de nombres de C ++? Tampoco estoy convencido todavía de que . sea ​​una mala elección.

. no se puede usar sin un cambio importante. Sería ambiguo en el lenguaje.

:: se usa en C ++ para espacios de nombres y en C # para prefijos de espacios de nombres ( global::System , por ejemplo).

Estaba pensando en una solución rápida sobre este tema, para resolver solo la herencia simple, pegue los archivos pegjs juntos, mientras tiene todo el espacio de nombres.

Esto puede hacer que las gramáticas sean demasiado detalladas e implica un paso de construcción, pero mirando el lado positivo, te obligaría a tener gramáticas DRY y OTW granulares

Y con respecto al marcado, no digo que esto sea adecuado para este hilo, sino solo una opción a considerar, estaba optando por un simple__

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

@andreineculau Básicamente, ya estoy haciendo esto con un paso de compilación, por lo que si usted y otros solo están buscando algo para generar analizadores útiles a partir de una gramática con un árbol de dependencia (donde se genera un solo analizador que implementa la gramática combinada), podría Limpiar lo que tengo y liberarlo para que la discusión pueda volver a enfocarse en cómo lidiar con esto de una manera más permanente.

Otra cosa: abordar esto principalmente diseñando extensiones a la sintaxis gramatical pasa por alto algo importante, que es que una de las principales razones por las que todos tenemos ganas de extraer reglas de otras gramáticas (otra es la claridad) es la necesidad de escribir analizadores que compartan mucha lógica. Entonces, mientras que los analizadores generados nunca pueden volver a componerse de manera significativa en el tiempo de análisis, parece importante que un árbol de gramáticas genere un árbol de analizadores, en lugar de un analizador monolítico. Es más importante cuando un conjunto de analizadores formará parte de una interfaz de usuario web, pero en general no está de más evitar un exceso innecesario en el código generado.

@odonnell +1 por publicar cualquier cosa, sin importar si tienes tiempo para limpiarlo

y +1 para la aclaración. Esto debe tratarse como una solución rápida, no como una solución adecuada a largo plazo.

@odonnell, mi opinión está en línea en https://github.com/andreineculau/core-pegjs . Por favor, dame un toque si tienes algo mejor.

+1 para esta función

: +1:

: +1:

: +1:

Fui y escribí un complemento / extensión para PEG.js que realiza importaciones: https://github.com/casetext/pegjs-import.

+1 para esto también.

Implemento esto en # 308 de manera genérica: la inclusión de gramática es solo una forma de implementar reglas de descomposición.

Gran característica: +1:

Espero verlo lanzado.

: +1:

¡Impresionante! : +1:

@dmajda Llego tarde a esta fiesta, pero me pregunto con qué frecuencia necesitamos importar muchas reglas de otra biblioteca. Me encantaría poder importar cosas como Url y Email en mis gramáticas compuestas, pero no me importa que Url también pueda tener cosas como HierarchicalPart y AsciiLetter . ¿Crees que algo como las exportaciones con nombre de Node sería una forma viable de avanzar, manteniendo los beneficios del espacio de nombres pero permitiendo importaciones directas con nombre?

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

Token
  = PhoneNumber
  / Url
  / SchemalessUrl

El espacio de nombres ha sido un problema para mí mientras trato de explorar la escritura de gramáticas componibles de otro modo. Estoy atascado en este momento al incluir archivos en archivos y nombrar cosas de la forma en que se nombraron las funciones PHP antes de que introdujeran los espacios de nombres adecuados: UrlIpHost , HtmlQuotedString , etc.

@dmajda @futagoza

¿Algún progreso en este tema? ¿O la discusión principal que vive ahora en el # 473?
Mi archivo de gramática está creciendo muy rápido :(
Sería bueno dividirlo en varios

No me importaría poder dividir gramáticas entre archivos, simplemente por organización y composición. ¿Los haría más fáciles de probar y reutilizar, además de proporcionar una forma de intercambiar gramáticas dinámicamente, tal vez? Solo algunos pensamientos.

El ejemplo de JavaScript que utilicé como base tiene más de 1300 líneas. Me tomó un tiempo aprender dónde estaba todo, saltar y editar diferentes secciones.

@mikeaustin Veo esta característica como una especie de Node.JS required :

cat bash.pegjs
{
const _ = require ("espacio en blanco");
const LB = require ("line_break");
const CodeBlock = require ("code_block");
const BoolExpr = require ("expresión_booleana");
}
...
IfStatement = "if" _ "[" BoolExpr "]" _ ";" _ "entonces" LB? CodeBlock "fi"

Estoy de acuerdo, dividir gramáticas y hacerlas modulares es una gran característica, sin embargo, manejar estos casos sería un problema:
1- subgramática que se basa en una variable global que se definió en el código gramatical principal?
2- ¿variables duplicadas y nombre gramatical?

En mi opinión, un enfoque conveniente temporalmente sería crear un nuevo complemento para PEG.js (independiente de PEG.js) que defina una palabra clave para importar (por ejemplo, @load (anotherGrammarFileLocation)) la palabra clave no debe formar parte de la gramática javacsript / peg.js,
cree una gramática reg-exp o peg para detectar esa palabra clave y sustitúyala por el contenido de "otra ubicación de archivo de gramática", y envíe el código sustituido a PEG.js

Ejemplo:

integers.pegjs

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

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

Tenga en cuenta que al usar este método, si alguien no definió la gramática de inicio y colocó @load antes de "arrayOfInteger", peg.js asumirá la primera gramática como el inicio (gramática de enteros)

Un enfoque para manejar esto es usar los mismos nombres de archivo y gramática inicial y dejar que el nuevo ad-don configure manualmente el atributo de inicio como nombre de archivo, o sustituya todo el contenido al final del archivo.

el usuario debe ser responsable de cualquier duplicación.

Solo quiero resaltar que este problema es principalmente una solicitud de optimización, porque la componibilidad / modularidad es algo que puede lograr por su cuenta, especialmente cuando controla el espectro completo de la gramática.

Si no se siente cómodo con una gramática de 1k líneas de largo, divídala y concatenela como mejor le parezca antes de convertirla en pegjs.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

mattkanwisher picture mattkanwisher  ·  5Comentarios

audinue picture audinue  ·  13Comentarios

dmajda picture dmajda  ·  15Comentarios

richb-hanover picture richb-hanover  ·  7Comentarios

marek-baranowski picture marek-baranowski  ·  6Comentarios