JavaScript es, sin un texto repetitivo personalizado , incapaz de manejar correctamente los caracteres / puntos de código Unicode fuera del BMP , es decir, aquellos cuya codificación requiere más de 16 bits.
Esta limitación parece trasladarse a PEG.js, como se muestra en el siguiente ejemplo.
En particular, me gustaría poder especificar rangos como [\u1D400-\u1D419]
(que actualmente se convierte en [ᵀ0-ᵁ9]
) o equivalentemente [𝐀-𝐙]
(que arroja un "rango de caracteres no válido" error). (Y usar la notación nueva de ES6 [\u{1D400}-\u{1D419}]
da como resultado el siguiente error: SyntaxError: Expected "!", "$", "&", "(", ".", character class, comment, end of line, identifier, literal, or whitespace but "[" found.
.)
¿Podría haber una forma de hacer que esto funcione que no requiera cambios en PEG.js?
Código de ejemplo:
Esta gramática:
//MathUpper = [𝐀-𝐙]+
MathUpperEscaped = [\u1D400-\u1D419]+
Comportamiento esperado:
El analizador generado a partir de la gramática dada analiza correctamente, por ejemplo, "𝐀𝐁𝐂".
Comportamiento real:
Un error de análisis: Line 1, column 1: Expected [ᵀ0-ᵁ9] but "
(O, al descomentar la otra regla, un error "Rango de caracteres no válido").
Para ser completamente honesto, además de actualizar el soporte Unicode para el analizador gramatical PEG.js y el ejemplo de JavaScript , tengo poco o ningún conocimiento sobre Unicode, por lo que actualmente no puedo solucionar este problema (está claramente establecido en ambas gramática: _Non -Los caracteres BMP se ignoran por completo_).
Por ahora, mientras trabajo en proyectos más urgentes relacionados con el personal y el trabajo (incluido _PEG.js 0.x_), seguiré esperando a que alguien que entienda mejor Unicode ofrezca algunas relaciones públicas 😆, o eventualmente lo haga después de _PEG. js v1_, lo siento amigo.
Para su información, los pares sustitutos parecen funcionar. La gramática
start = result:[\uD83D\uDCA9]+ {return result.join('')}
analiza 💩 que es u + 1F4A9. Tenga en cuenta que result.join ('') vuelve a juntar el par sustituto, de lo contrario obtendrá ['\uD83D','\uDCA9']
lugar de hankey. Los rangos serían problemáticos.
Más sobre pares sustitutos: https://en.wikipedia.org/wiki/UTF-16#U + 010000_to_U + 10FFFF
Esto de ninguna manera reemplaza lo que pidió el OP.
@drewnolan Gracias por el aviso 👍
Desafortunadamente, esa gramática también analiza \uD83D\uD83D
.
Para otros que se han encontrado con este problema: tengo la suerte de que solo necesito manejar un pequeño subconjunto de puntos de código fuera del BMP, así que terminé mapeándolos en el área de uso privado del BMP antes de analizar e invertir este mapeo inmediatamente después. .
Esta solución está obviamente plagada de problemas en el caso general, pero funciona bien en mi dominio de problemas.
@futagoza - Haré todo lo posible para explicarlo. Te enfrentas a varios problemas aquí.
utf-16
, ucs4
, etcétera. Ésta es la forma en que codepoints
, que son los datos previstos, se codifican como bytes. utf-16-le
por ejemplo le permite codificar la mayoría de las letras como pares de dos bytes llamados code units
, pero use grupos de unidades de código para expresar caracteres de alto valor hasta 0x10ffff
.Yo también quiero esto, pero siendo realistas, esto no va a suceder.
mierda, alguien cometió un reemplazo de analizador de cadenas completo hace casi un año , y reconocieron la sobrecarga, por lo que nos dejaron usar cadenas JS estándar por lo general
POR QUÉ NO ES ESO FUSIONADO
@StoneCypher ¡Amo el fuego en tu corazón! Pero, ¿por qué hacerle pasar un mal rato al responsable de mantenimiento actual? A nadie se le debe nada. ¿Por qué no mantener tu propia bifurcación?
No hay un mantenedor actual. La persona que se hizo cargo de PEG nunca ha publicado nada. Trabajó en el siguiente menor durante tres años, luego dijo que no le gustaba cómo se veía, que estaba tirando todo peg.js
y comenzando de nuevo con algo que escribió desde cero en un idioma diferente, con un idioma incompatible. AST.
La herramienta ha perdido la mitad de su base de usuarios esperando tres años a que este hombre cometiera arreglos de una línea que otras personas escribieron, como soporte para módulo es6, soporte para mecanografiado, soporte para flechas, unicode extendido, etcétera.
Hay una docena de personas que le piden que se fusione y él sigue diciendo "no, este es mi proyecto de hobby ahora y no me gusta lo que es".
Mucha gente tiene empresas basadas en este analizador. Están completamente jodidos.
Este hombre prometió ser un mantenedor de una herramienta extremadamente importante y no ha realizado ningún mantenimiento. Es hora de dejar que otra persona mantenga esta biblioteca en buen estado ahora.
¿Por qué no mantener tu propia bifurcación?
Lo tengo desde hace tres años. Mi peg
tiene casi un tercio del rastreador de problemas solucionado.
Tuve que clonarlo, cambiarle el nombre y hacer una nueva bifurcación para solucionar el problema de tamaño para intentar confirmarlo, porque el mío se ha desviado demasiado.
Es hora de que todos los demás reciban estas correcciones, así como las que han estado en el rastreador desde 2017.
Este tipo no está manteniendo la vinculación; lo está dejando morir.
Es tiempo de cambiar.
@drewnolan : no estoy seguro de si esto es interesante o no, pero los pares sustitutos no funcionan en realidad. Es solo que, por casualidad, suelen hacerlo.
Para comprender el problema subyacente, debe pensar en el patrón de bits del nivel de codificación, no en el nivel de representación uno.
Es decir, si tiene un valor Unicode de 240, la mayoría de la gente pensará "Oh, se refiere a 0b1111 0000
". Pero en realidad, no es así como Unicode representa 240; más de 127 está representado por dos bytes, porque el bit superior es un indicador, no un bit de valor. Entonces, 240 en Unicode es en realidad 0b0000 0001 0111 0000
en almacenamiento (excepto en utf-7, que es real y no es un error tipográfico, donde las cosas se ponen muy raras. Y sí, sé que Wikipedia dice que no está en uso. Wikipedia está mal . Es lo que se envía SMS; podría ser la codificación de caracteres más común por tráfico total).
Así que aquí está el problema.
Si escribe un byte STUV WXYZ, luego en utf16, a partir de los datos de ucs4, si su cosa se corta a la mitad, muy a menudo puede volver a engraparla.
Una vez en 128 no se puede, para caracteres de forma nativa con codificación de dos bytes. (Suena como un número terriblemente específico, ¿no?)
Porque cuando ese bit superior en la posición del segundo byte está en uso, cortarlo por la mitad agregará un cero donde debería haber sido uno. Volver a engraparlos uno al lado del otro como datos binarios no elimina el concepto de valor nuevamente. El valor decodificado no es equivalente al valor codificado y está agregando decodificaciones, no codificaciones.
Da la casualidad de que la mayoría de los emoji están fuera de este rango. Sin embargo, gran parte de varios idiomas no lo son, incluido el chino poco común, la mayoría de los símbolos matemáticos y musicales.
Por supuesto, su enfoque es lo suficientemente bueno para casi todos los emoji y para todos los lenguajes humanos lo suficientemente comunes como para ingresar a Unicode 6, lo cual es una gran mejora con respecto al status quo.
Pero este PR realmente debería fusionarse, una vez que se haya probado lo suficiente tanto para verificar su corrección como frente a problemas de rendimiento inesperados (recuerde, los problemas de rendimiento de Unicode son la razón por la que php murió)
Parece que la expresión . (dot character)
también necesita el modo Unicode. Comparar:
const string = '-🐎-👱-';
const symbols = (string.match(/./gu));
console.log(JSON.stringify(symbols, null, ' '));
const pegResult = require('pegjs/')
.generate('root = .+')
.parse(string);
console.log(JSON.stringify(pegResult, null, ' '));
Producción:
[
"-",
"🐎",
"-",
"👱",
"-"
]
[
"-",
"\ud83d",
"\udc0e",
"-",
"\ud83d",
"\udc71",
"-"
]
Trabajé recientemente en esto, usando # 616 como base y modificándolo para usar la sintaxis ES6 \u{hhhhhhh}
, crearé un PR en algunas horas.
Calcular los rangos de expresiones regulares UTF-16 divididos por sustitutos es un poco complicado y usé https://github.com/mathiasbynens/regenerate para esto; esta sería la primera dependencia del paquete pegjs, espero que sea posible (también hay polyfills para propiedades Unicode que podrían agregarse como dependencia, ver # 648). Consulte Wikipedia si no conoce los sustitutos UTF-16 .
Para que PEG.js sea compatible con todo Unicode, existen varios niveles:
.
para capturar 1 o 2 unidades de código.Para la mayoría de los puntos podemos lograr ser compatibles con versiones anteriores y generar analizadores muy similares a los anteriores, excepto en el punto 5 porque el resultado de un análisis puede depender de si la regla de los puntos captura una o dos unidades de código. Para esto, propongo agregar una opción de tiempo de ejecución para permitir que el usuario elija entre dos o tres opciones:
La clase Regex se puede analizar estáticamente durante la generación del analizador para verificar si tienen una longitud fija (en número de unidades de código). Hay 3 casos: 1. solo un BMP o una sola unidad de código, o 2. solo dos unidades de código, o 3. una o dos unidades de código dependiendo del tiempo de ejecución. Por ahora, el código de bytes asume que una clase de expresiones regulares es siempre una unidad de código ( ver aquí ). Con el análisis estático, podríamos cambiar este parámetro de esta instrucción de código de bytes a 1 o 2 para los dos primeros casos. Pero para el tercer caso, supongo que se debe agregar una nueva instrucción de código de bytes para, en tiempo de ejecución, obtener el número de unidades de código coincidentes y aumentar el cursor en consecuencia. Otras opciones sin una nueva instrucción de código de bytes serían: 1. calcular siempre el número de unidades de código coincidentes, pero esto es una penalización de rendimiento durante el análisis para analizadores solo de BMP, por lo que no me gusta esta opción; 2. para calcular si la unidad de código actual es un sustituto alto seguido de un sustituto bajo hasta un incremento de 1 o 2, pero esto supondría que la gramática siempre tiene sustitutos UTF-16 bien formados sin la posibilidad de escribir gramáticas con sustitutos solitarios ( consulte el siguiente punto) y esto también es una penalización de rendimiento para los analizadores solo de BMP.
Está la cuestión de los sustitutos solitarios (sustituto alto sin sustituto bajo después, o sustituto bajo sin sustituto alto antes). Mi opinión sobre esto es que una clase de expresiones regulares debe ser exclusivamente: ya sea con sustitutos solitarios con caracteres Unicode UTF-16 bien formados (BMP o un sustituto alto seguido de un sustituto bajo), de lo contrario, existe el peligro de que los autores gramaticales desconozcan Las sutilezas de UTF-16 mezclan ambos y no entienden el resultado, y los autores de gramática que quieran manejarse por sí mismos sustitutos de UTF-16 pueden hacerlo con las reglas de PEG para describir los vínculos entre sustitutos específicos altos y bajos. Propongo agregar un visitante que haga cumplir esta regla durante la generación del analizador.
Para concluir, probablemente sea más fácil manejar la cuestión de los sustitutos solitarios en PEG que en regex porque el analizador de PEG siempre avanza, por lo que se reconoce la siguiente unidad de código o no lo es, al contrario de las expresiones regulares donde posiblemente se podría asociar algún retroceso o disociar un sustituto alto con un sustituto bajo y, en consecuencia, cambiar el número de caracteres Unicode coincidentes, etc.
El PR para la sintaxis de ES6 para el carácter Unicode astral es # 651 basado en # 616 y el desarrollo de clases es https://github.com/Seb35/pegjs/commit/0d33a7a4e13b0ac7c55a9cfaadc16fc0a5dd5f0c implementando los puntos 2 y 3 en mi comentario anterior, y solo un truco rápido para el incremento del cursor (punto 4) y nada por ahora para la regla punto .
(punto 5).
Mi desarrollo actual sobre este problema está casi terminado, el trabajo más avanzado está en https://github.com/Seb35/pegjs/tree/dev-astral-classes-final. Los cinco puntos mencionados anteriormente se tratan y el comportamiento global intenta imitar las expresiones regulares de JS con respecto a los casos extremos (y hay muchos de ellos).
El comportamiento global se rige por la opción unicode
similar a la bandera unicode
en expresiones regulares JS: el cursor aumenta de 1 carácter Unicode (1 o 2 unidades de código) dependiendo del texto real (p. Ej. [^a]
coincide con el texto "💯" y el cursor se incrementa en 2 unidades de código). Cuando la opción unicode
es falsa, el cursor siempre aumenta en 1 unidad de código.
Con respecto a la entrada, no estoy seguro de si diseñamos PEG.js de la misma manera que las expresiones regulares de JS: ¿deberíamos autorizar [\u{1F4AD}-\u{1F4AF}]
(equivalente a [\uD83D\uDCAD-\uD83D\uDCAF]
) en la gramática en modo no Unicode? Podemos diferenciar entre "entrada Unicode" y "salida Unicode":
Personalmente, supongo que preferiría que autoricemos la entrada Unicode, ya sea de forma permanente o con una opción con un valor predeterminado true
ya que no hay una sobrecarga significativa y esto habilitaría esta posibilidad para todos de forma predeterminada, pero el Unicode de salida debería permanecer false
porque el rendimiento de los analizadores generados es mejor (siempre un incremento de cursor de 1).
Respecto a este tema en general (y sobre la salida Unicode por defecto a false
), debemos tener en cuenta que ya es posible codificar en nuestras gramáticas caracteres Unicode, al precio de comprender el funcionamiento de UTF-16 :
// rule matching [\u{1F4AD}-\u{1F4AF}]
my_class = "\uD83D" [\uDCAD-\uDCAF]
// rule matching any Unicode character
my_strict_unicode_dot_rule = $( [\u0000-\uD7FF\uE000-\uFFFF] / [\uD800-\uDBFF] [\uDC00-\uDFFF] )
// rule matching any Unicode character or a lone surrogate
my_loose_unicode_dot_rule = $( [\uD800-\uDBFF] [\uDC00-\uDFFF]? / [\u0000-\uFFFF] )
Entonces, un autor de gramática que quiera un analizador rápido y sea capaz de reconocer caracteres Unicode en partes específicas de su gramática puede usar dicha regla. En consecuencia, este problema se trata simplemente de simplificar la administración de Unicode sin sumergirse en los componentes internos de UTF-16.
Sobre la implementación, consideré en mi primer intento que el texto gramatical estaba codificado en caracteres Unicode y que la regla 'punto' del analizador PEG.js reconocía caracteres Unicode. El segundo y último intento revirtió esto (la regla del punto es siempre 1 unidad de código para un análisis más rápido) y hay un pequeño algoritmo en el visitante prepare-unicode-classes.js para reconstruir caracteres Unicode divididos en clases de caracteres (por ejemplo, [\uD83D\uDCAD-\uD83D\uDCAF]
se reconoce sintácticamente como [ "\uD83D", [ "\uDCAD", "\uD83D" ], "\uDCAF" ]
y este algoritmo lo transforma en [ [ "\uD83D\uDCAD", "\uD83D\uDCAF" ] ]
). Tenía la intención de escribir esto en la propia gramática, pero habría sido largo y, lo que es más importante, hay varias formas de codificar los caracteres ("💯", "uD83DuDCAF", "u {1F4AF}"), por lo que es más fácil escribirlo. en un visitante.
Agregué dos códigos de operación en el segundo intento:
(input.charCodeAt(currPos) & 0xFC00) === 0xD800 && input.length > currPos + 1 && (input.charCodeAt(currPos+1) & 0xFC00) === 0xDC00
classes[c].test(input.substring(currPos, currPos+2)
ACCEPT_N
operación Hice algunas optimizaciones con la función "match", eliminando durante la generación las rutas de "código muerto" según el modo (Unicode o no) y la clase de caracteres.
Tenga en cuenta también que las expresiones regulares son siempre positivas en esta implementación: las expresiones regulares invertidas devuelven lo contrario, lo que da como resultado el código de bytes. Esto fue más fácil para evitar casos extremos alrededor de sustitutos.
Escribí algo de documentación, pero probablemente agregaré más (tal vez una guía para explicar rápidamente los detalles de las opciones unicode y los fragmentos con la regla de punto Unicode hecha en casa). Agregaré pruebas antes de enviarlo como PR.