¿Qué hace que Java sea más fácil de analizar que C?

90

Estoy familiarizado con el hecho de que las gramáticas de C y C ++ son sensibles al contexto y , en particular, necesita un "truco lexer" en C. Por otro lado, tengo la impresión de que puede analizar Java con solo 2 señales de anticipación, a pesar de la considerable similitud entre los dos idiomas.

¿Qué tendría que cambiar acerca de C para que sea más manejable de analizar?

Lo pregunto porque todos los ejemplos que he visto de la sensibilidad al contexto de C son técnicamente permitidos pero terriblemente extraños. Por ejemplo,

foo (a);

podría estar llamando a la función void foocon argumento a. O bien, podría declarar aser un objeto de tipo foo, pero podría deshacerse fácilmente de las paréntesis. En parte, esta rareza se produce porque la regla de producción del "declarador directo" para la gramática C cumple el doble propósito de declarar tanto funciones como variables.

Por otro lado, la gramática de Java tiene reglas de producción independientes para la declaración de variables y la declaración de funciones. Si tú escribes

foo a;

entonces sabrá que es una declaración de variable y foopuede analizarse sin ambigüedades como un nombre de tipo. Es posible que este no sea un código válido si la clase foono se ha definido en algún lugar del alcance actual, pero ese es un trabajo para el análisis semántico que se puede realizar en una pasada del compilador posterior.

Lo he visto decir que C es difícil de analizar debido a typedef, pero también puede declarar sus propios tipos en Java. ¿Qué reglas gramaticales de C, además direct_declarator, tienen fallas?

Korrok
fuente
7
Buena pregunta. Sin embargo, probablemente sea demasiado amplio o principalmente obstinado.
asteri
37
Esta es una pregunta válida sobre los analizadores sintácticos y lo único amplio o basado en opiniones al respecto son las últimas dos oraciones (que probablemente deberían eliminarse o cambiarse). Renunciar con los votos cerrados.
R .. GitHub DEJA DE AYUDAR A ICE
1
Edité la pregunta en consecuencia, gracias a @R .. por los comentarios.
korrok
3
Prácticamente todos los lenguajes informáticos (estándar) son sensibles al contexto ; no se puede declarar una variable de un tipo y utilizarla incorrectamente en la mayoría de los idiomas . Eso es diferente a que "todas las gramáticas del idioma" son sensibles al contexto; la mayoría de las personas que crean analizadores crean un analizador sin contexto (o incluso más restrictivo) y luego usan hacks fuera del analizador para verificar las propiedades libres de contexto.
Ira Baxter
1
@IraBaxter Yo no llamaría a eso "hacks". Dividir el problema en dos parece algo razonable, ya que el análisis de los lenguajes sensibles al contexto no se puede hacer de manera eficiente (y de hecho, incluso el análisis de los lenguajes sin contexto no es eficiente, y es por eso que generalmente nos restringimos a subconjuntos de idiomas sin contexto) . Un análisis sintáctico + estático sin contexto para verificar solo las propiedades sensibles al contexto sobre el AST es algo razonable.
Bakuriu

Respuestas:

76

Analizar C ++ se está volviendo difícil. Analizar Java se está volviendo tan difícil.

Vea esta respuesta SO que discute por qué C (y C ++) es "difícil" de analizar . El breve resumen es que las gramáticas C y C ++ son intrínsecamente ambiguas; le darán múltiples análisis y debe usar el contexto para resolver las ambigüedades. Entonces, la gente comete el error de asumir que tiene que resolver las ambigüedades mientras analiza; no es así, ver más abajo. Si insiste en resolver ambigüedades mientras analiza, su analizador se vuelve más complicado y mucho más difícil de construir; pero esa complejidad es una herida autoinfligida.

IIRC, la gramática "obvia" de LALR (1) de Java 1.4 no era ambigua, por lo que era "fácil" de analizar. No estoy tan seguro de que el Java moderno no tenga al menos ambigüedades locales de larga distancia; siempre existe el problema de decidir si "... >>" cierra dos plantillas o es un "operador de turno a la derecha". Sospecho que Java moderno ya no analiza con LALR (1) .

Pero se puede superar el problema del análisis sintáctico mediante el uso de analizadores potentes (o analizadores débiles y hacks de recopilación de contextos como lo hacen en la actualidad las interfaces de C y C ++), para ambos lenguajes. C y C ++ tienen la complicación adicional de tener un preprocesador; estos son más complicados en la práctica de lo que parecen. Una afirmación es que los analizadores de C y C ++ son tan difíciles que tienen que escribirse a mano. No es verdad; puede construir analizadores Java y C ++ muy bien con generadores de analizadores GLR.

Pero el análisis no es realmente el problema.

Una vez que analice, querrá hacer algo con el árbol AST / parse. En la práctica, es necesario saber, para cada identificador, cuál es su definición y dónde se usa ("resolución de nombre y tipo", descuidadamente, construyendo tablas de símbolos). Esto resulta ser MUCHO más trabajo que hacer que el analizador sea correcto, agravado por la herencia, las interfaces, la sobrecarga y las plantillas, y lo confuso por el hecho de que la semántica de todo esto está escrita en un lenguaje natural informal que se extiende a lo largo de decenas a cientos de páginas. del estándar de idioma. C ++ es realmente malo aquí. Java 7 y 8 se están volviendo bastante horribles desde este punto de vista. (Y las tablas de símbolos no son todo lo que necesita; vea mi biografía para un ensayo más largo sobre "La vida después del análisis").

La mayoría de las personas luchan con la parte del análisis puro (a menudo nunca termina; compruebe el SO mismo para ver las muchas, muchas preguntas sobre cómo crear analizadores que funcionan para idiomas reales), por lo que nunca ven la vida después del análisis. Y luego obtenemos teoremas populares sobre lo que es difícil de analizar y no hay señales sobre lo que sucede después de esa etapa.

Arreglar la sintaxis de C ++ no lo llevará a ninguna parte.

Con respecto a cambiar la sintaxis de C ++: encontrará que necesita parchear muchos lugares para ocuparse de la variedad de ambigüedades locales y reales en cualquier gramática de C ++. Si insiste, la siguiente lista podría ser un buen punto de partida . Sostengo que no tiene sentido hacer esto si usted no es el comité de estándares de C ++; si lo hicieras y construyeras un compilador usando eso, nadie en su sano juicio lo usaría. Se ha invertido demasiado en las aplicaciones C ++ existentes como para cambiarlas por conveniencia de los tipos que crean analizadores; además, su dolor ha terminado y los analizadores existentes funcionan bien.

Es posible que desee escribir su propio analizador. OK eso está bien; simplemente no espere que el resto de la comunidad le permita cambiar el idioma que deben usar para que sea más fácil para usted. Todos quieren que sea más fácil para ellos, y eso es usar el lenguaje tal como está documentado e implementado.

Ira Baxter
fuente
Buena respuesta. Consulte también D y C +, que intentan resolver algunos de estos problemas. s / content /
contend
3
He leído Life After Parsing antes y descubrí que es una verdadera revelación; me dejó claro que hay mucho más trabajo en el análisis semántico (resolución de nombre / tipo, ...) que en el análisis. Estoy no tratar de cambiar la sintaxis de cualquier idioma. Yo no quiero entender lo que las propiedades son de un idioma en el que se puede hacer el análisis sintáctico primero y luego el análisis semántico. C no es un lenguaje de este tipo (necesita un hack de lexer); Siempre pensé que Java lo era y quiero saber por qué.
korrok
1
@Korrok: lea mi respuesta sobre la construcción de Java / C ++ con analizadores GLR. No necesitas ningún truco lexer . Entonces, la distinción está en la mente de las personas que están usando la tecnología de análisis incorrecta. ... Por supuesto, construir una interfaz completa de C ++ (especialmente C ++ 14, que hemos hecho) es más difícil que hacer Java8, pero ambos son difíciles (en términos de esfuerzo y prestar atención a los detalles) y analizar es la pieza más fácil.
Ira Baxter
1
Estoy de acuerdo con su "Vida después del análisis": por ejemplo, la resolución de sobrecarga en C # puede codificar cualquier problema de 3-SAT y, por lo tanto, es NP-hard.
Jörg W Mittag