yyparse usar tutorial realiza que programacion manejo lexico funcion errores ejemplos como analizador actividades r yacc

usar - ¿Cómo exactamente R analiza `->`, el operador de asignación correcta?



que es bison (1)

Entonces, esta es una pregunta trivial, pero me molesta que no pueda responderla, y tal vez la respuesta me enseñe algunos detalles más sobre cómo funciona R.

El título lo dice todo: ¿cómo analiza R -> , la oscura función de asignación del lado derecho?

Mis trucos habituales para sumergirme en esto fallaron:

`->`

Error: objeto -> no encontrado

getAnywhere("->")

no se encontró ningún objeto llamado ->

Y no podemos llamarlo directamente:

`->`(3,x)

Error: no se pudo encontrar la función "->"

Pero, por supuesto, funciona:

(3 -> x) #assigns the value 3 to the name x # [1] 3

Parece que R sabe cómo simplemente revertir los argumentos, pero pensé que los enfoques anteriores seguramente habrían resuelto el caso:

pryr::ast(3 -> y) # /- () # /- `<- #R interpreter clearly flipped things around # /- `y # (by the time it gets to `ast`, at least...) # /- 3 # (note: this is because `substitute(3 -> y)` # # already returns the reversed version)

Compare esto con el operador de asignación regular:

`<-` .Primitive("<-") `<-`(x, 3) #assigns the value 3 to the name x, as expected

?"->" ?assignOps , y la Definición del lenguaje R simplemente lo mencionan de pasada como el operador de asignación correcto.

Pero claramente hay algo único acerca de cómo -> se usa. No es una función / operador (como parecen demostrar las llamadas a getAnywhere y directamente a `->` ), entonces, ¿qué es? ¿Está completamente en una clase propia?

¿Hay algo que aprender de esto además de " -> es completamente único dentro del lenguaje R en cómo se interpreta y maneja; memorizar y seguir adelante"?


Permítanme presentar esto diciendo que no sé absolutamente nada sobre cómo funcionan los analizadores sintácticos. Dicho esto, la línea 296 de gram.y define los siguientes tokens para representar la asignación en los usos del analizador R (¿YACC?):

%token LEFT_ASSIGN EQ_ASSIGN RIGHT_ASSIGN LBB

Luego, en las líneas 5140 a 5150 de gram.c , esto se parece al código C correspondiente:

case ''-'': if (nextchar(''>'')) { if (nextchar(''>'')) { yylval = install_and_save2("<<-", "->>"); return RIGHT_ASSIGN; } else { yylval = install_and_save2("<-", "->"); return RIGHT_ASSIGN; } }

Finalmente, comenzando en la línea 5044 de gram.c , la definición de install_and_save2 :

/* Get an R symbol, and set different yytext. Used for translation of -> to <-. ->> to <<- */ static SEXP install_and_save2(char * text, char * savetext) { strcpy(yytext, savetext); return install(text); }

De nuevo, al tener cero experiencia trabajando con analizadores sintéticos, parece que -> y ->> se traducen directamente en <- y <<- , respectivamente, a un nivel muy bajo en el proceso de interpretación.

Trajo un muy buen punto al preguntar cómo el analizador "sabe" invertir los argumentos a -> , considerando que -> parece estar instalado en la tabla de símbolos R como <- - y así poder interpretar correctamente x -> y como y <- x y no x <- y . Lo mejor que puedo hacer es proporcionar más especulaciones a medida que continúo encontrando "evidencia" para respaldar mis afirmaciones. Con suerte, algún experto misericordioso de YACC tropezará con esta pregunta y proporcionará una pequeña idea; Sin embargo, no voy a contener la respiración.

Volviendo a las líneas 383 y 384 de gram.y , esto parece una lógica de análisis más relacionada con los símbolos LEFT_ASSIGN y RIGHT_ASSIGN mencionados LEFT_ASSIGN :

| expr LEFT_ASSIGN expr { $$ = xxbinary($2,$1,$3); setId( $$, @$); } | expr RIGHT_ASSIGN expr { $$ = xxbinary($2,$3,$1); setId( $$, @$); }

Aunque realmente no puedo hacer cara o cruz de esta sintaxis loca, noté que el segundo y el tercer argumento de xxbinary se xxbinary a WRT LEFT_ASSIGN ( xxbinary($2,$1,$3) ) y RIGHT_ASSIGN ( xxbinary($2,$3,$1) ).

Esto es lo que estoy imaginando en mi cabeza:

LEFT_ASSIGN Escenario: y <- x

  • $2 es el segundo "argumento" para el analizador en la expresión anterior, es decir, <-
  • $1 es el primero; a saber, y
  • $3 es el tercero; x

Por lo tanto, la llamada resultante (C?) Sería xxbinary(<-, y, x) .

Aplicando esta lógica a RIGHT_ASSIGN , es decir, x -> y , combinado con mi conjetura anterior sobre <- y -> intercambiando,

  • $2 se traducen de -> a <-
  • $1 es x
  • $3 es y

Pero como el resultado es xxbinary($2,$3,$1) lugar de xxbinary($2,$1,$3) , el resultado sigue siendo xxbinary(<-, y, x) .

Partiendo de esto un poco más, tenemos la definición de xxbinary en la línea 3310 de gram.c :

static SEXP xxbinary(SEXP n1, SEXP n2, SEXP n3) { SEXP ans; if (GenerateCode) PROTECT(ans = lang3(n1, n2, n3)); else PROTECT(ans = R_NilValue); UNPROTECT_PTR(n2); UNPROTECT_PTR(n3); return ans; }

Desafortunadamente, no pude encontrar una definición adecuada de lang3 (o sus variantes lang1 , lang2 , etc.) en el código fuente R, pero supongo que se usa para evaluar funciones especiales (es decir, símbolos) de una manera que está sincronizado con el intérprete.

Actualizaciones Intentaré abordar algunas de sus preguntas adicionales en los comentarios lo mejor que pueda, dado mi (muy) limitado conocimiento del proceso de análisis.

1) ¿Es realmente el único objeto en R que se comporta así? (Tengo en mente la cita de John Chambers a través del libro de Hadley: "Todo lo que existe es un objeto. Todo lo que sucede es una llamada a la función". Esto claramente se encuentra fuera de ese dominio, ¿hay algo más como esto?

Primero, estoy de acuerdo en que esto se encuentra fuera de ese dominio. Creo que la cita de Chambers se refiere al entorno R, es decir, procesos que tienen lugar después de esta fase de análisis de bajo nivel. Sin embargo, tocaré esto un poco más abajo. De todos modos, el único otro ejemplo de este tipo de comportamiento que pude encontrar es el operador ** , que es sinónimo del operador de exponenciación más común ^ . Al igual que con la asignación correcta, ** no parece ser "reconocido" como una llamada de función, etc., por el intérprete:

R> `->` #Error: object ''->'' not found R> `**` #Error: object ''**'' not found

Encontré esto porque es el único otro caso donde install_and_save2 es usado por el analizador C :

case ''*'': /* Replace ** by ^. This has been here since 1998, but is undocumented (at least in the obvious places). It is in the index of the Blue Book with a reference to p. 431, the help for ''Deprecated''. S-PLUS 6.2 still allowed this, so presumably it was for compatibility with S. */ if (nextchar(''*'')) { yylval = install_and_save2("^", "**"); return ''^''; } else yylval = install_and_save("*"); return c;

2) ¿Cuándo sucede exactamente esto? Tengo en cuenta que el sustituto (3 -> y) ya ha cambiado la expresión; No pude deducir de la fuente qué sustituto hace que hubiera afectado al YACC ...

Por supuesto, todavía estoy especulando aquí, pero sí, creo que podemos asumir con seguridad que cuando llamas a substitute(3 -> y) , desde la perspectiva de la función sustituto , la expresión siempre fue y <- 3 ; por ejemplo, la función ignora por completo que usted escribió 3 -> y . do_substitute , como el 99% de las funciones de C utilizadas por R, solo maneja argumentos SEXP , un EXPRSXP en el caso de 3 -> y (== y <- 3 ), creo. Esto es a lo que me refería anteriormente cuando hice una distinción entre el R Environment y el proceso de análisis. No creo que haya nada que active específicamente el analizador para que entre en acción, sino que todo lo que ingresas en el intérprete se analiza. Leí un poco más sobre el generador de analizador YACC / Bison anoche, y como lo entiendo (también conocido como no apostar la granja en esto), Bison usa la gramática que usted define (en el (los) archivo (s) .y ) generar un analizador en C, es decir, una función C que realiza el análisis real de la entrada. A su vez, todo lo que ingresa en una sesión R se procesa primero por esta función de análisis C, que luego delega la acción apropiada que se tomará en el Entorno R (por cierto, estoy usando este término de manera muy flexible). Durante esta fase, lhs -> rhs se traducirá a rhs <- lhs , ** a ^ , etc ... Por ejemplo, este es un extracto de una de las tablas de funciones primitivas en nombres.c :

/* Language Related Constructs */ /* Primitives */ {"if", do_if, 0, 200, -1, {PP_IF, PREC_FN, 1}}, {"while", do_while, 0, 100, 2, {PP_WHILE, PREC_FN, 0}}, {"for", do_for, 0, 100, 3, {PP_FOR, PREC_FN, 0}}, {"repeat", do_repeat, 0, 100, 1, {PP_REPEAT, PREC_FN, 0}}, {"break", do_break, CTXT_BREAK, 0, 0, {PP_BREAK, PREC_FN, 0}}, {"next", do_break, CTXT_NEXT, 0, 0, {PP_NEXT, PREC_FN, 0}}, {"return", do_return, 0, 0, -1, {PP_RETURN, PREC_FN, 0}}, {"function", do_function, 0, 0, -1, {PP_FUNCTION,PREC_FN, 0}}, {"<-", do_set, 1, 100, -1, {PP_ASSIGN, PREC_LEFT, 1}}, {"=", do_set, 3, 100, -1, {PP_ASSIGN, PREC_EQ, 1}}, {"<<-", do_set, 2, 100, -1, {PP_ASSIGN2, PREC_LEFT, 1}}, {"{", do_begin, 0, 200, -1, {PP_CURLY, PREC_FN, 0}}, {"(", do_paren, 0, 1, 1, {PP_PAREN, PREC_FN, 0}},

Notará que -> , ->> y ** no están definidos aquí. Hasta donde yo sé, las expresiones primitivas R como <- y [ , etc ... son la interacción más cercana que R Environment tiene con cualquier código C subyacente. Lo que sugiero es que en esta etapa del proceso (desde que escribes un conjunto de caracteres en el intérprete y presionas ''Enter'', hasta la evaluación real de una expresión R válida), el analizador ya ha trabajado su magia, por eso no puede obtener una definición de función para -> o ** al rodearlos con backticks, como normalmente puede hacerlo.