Ответ 1
Позвольте мне предисловие к этому, сказав, что я абсолютно ничего не знаю о том, как работают парсеры. Сказав, что строка 296 графа .y определяет следующие токены для представления присваивания в синтаксическом анализаторе (YACC?) R:
%token LEFT_ASSIGN EQ_ASSIGN RIGHT_ASSIGN LBB
Затем в строках с 5140 по 5150 грамм .c это выглядит как соответствующий код C:
case '-':
if (nextchar('>')) {
if (nextchar('>')) {
yylval = install_and_save2("<<-", "->>");
return RIGHT_ASSIGN;
}
else {
yylval = install_and_save2("<-", "->");
return RIGHT_ASSIGN;
}
}
Наконец, начиная с строки 5044 грамм .c, определение 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);
}
Итак, имея нулевой опыт работы с анализаторами, кажется, что ->
и ->>
переводятся непосредственно в <-
и <<-
соответственно на очень низком уровне в процессе интерпретации.
Вы очень хорошо поняли, как синтаксический анализатор "знает", чтобы изменить аргументы на ->
- считая, что ->
, по-видимому, установлен в таблицу символов R как <-
- и, таким образом, сможет правильно интерпретировать x -> y
как y <- x
, а не x <- y
. Лучшее, что я могу сделать, это дать дополнительные предположения, поскольку я продолжаю сталкиваться с "доказательствами", чтобы поддержать мои претензии. Надеюсь, какой-то милосердный эксперт YACC наткнется на этот вопрос и предоставит мало понимания; Хотя я не собираюсь задерживать дыхание.
Вернемся к строкам 383 и 384 графа .y, это выглядит как еще одна логика синтаксического анализа, связанная с вышеупомянутыми символами LEFT_ASSIGN
и RIGHT_ASSIGN
:
| expr LEFT_ASSIGN expr { $$ = xxbinary($2,$1,$3); setId( $$, @$); }
| expr RIGHT_ASSIGN expr { $$ = xxbinary($2,$3,$1); setId( $$, @$); }
Хотя я не могу сделать головы или хвосты этого сумасшедшего синтаксиса, я заметил, что второй и третий аргументы xxbinary
меняются на WRT LEFT_ASSIGN
(xxbinary($2,$1,$3)
) и RIGHT_ASSIGN
(xxbinary($2,$3,$1)
).
Вот что я изображаю в своей голове:
LEFT_ASSIGN
Сценарий: y <- x
-
$2
- второй "аргумент" парсеру в указанном выше выражении, т.е.<-
-
$1
является первым; а именноy
-
$3
является третьим;x
Следовательно, результирующий (C?) вызов будет xxbinary(<-, y, x)
.
Применяя эту логику к RIGHT_ASSIGN
, т.е. x -> y
, в сочетании с моей более ранней гипотезой о <-
и ->
, заменяемой,
-
$2
переводится с->
в<-
-
$1
x
-
$3
y
Но так как результат xxbinary($2,$3,$1)
вместо xxbinary($2,$1,$3)
, результат все равно xxbinary(<-, y, x)
.
Построим это немного дальше, мы имеем определение xxbinary
на строке 3310 графа .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;
}
К сожалению, я не смог найти правильное определение lang3
(или его вариантов lang1
, lang2
и т.д.) в исходном коде R, но я предполагаю, что он используется для оценки специальных функции (т.е. символы) таким образом, который синхронизирован с интерпретатором.
Обновление Я попытаюсь рассмотреть некоторые из ваших дополнительных вопросов в комментариях, так как лучше всего могу дать (очень) ограниченное знание процесса синтаксического анализа.
1) Действительно ли это единственный объект в R, который ведет себя так? (Я имел в виду цитату Джона Чамберса через книгу Хэдли: "Все что существует объект. Все, что происходит, это вызов функции. Это явно лежит вне этой области - есть ли что-нибудь еще, как это?
Во-первых, я согласен, что это лежит за пределами этого домена. Я считаю, что цитата из Chambers относится к среде R, т.е. К процессам, которые происходят после этой фазы анализа на низком уровне. Однако я коснусь этого чуть ниже. В любом случае, единственным примером такого поведения я мог бы найти оператор **
, который является синонимом более распространенного оператора возведения в степень ^
. Как и при правильном назначении, **
не представляется "признанным" вызовом функции и т.д. Интерпретатором:
R> `->`
#Error: object '->' not found
R> `**`
#Error: object '**' not found
Я нашел это, потому что это единственный случай, когда install_and_save2
используется парсером 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) Когда именно это происходит? Я имею в виду, что замена (3 → y) уже перевернул выражение; Я не мог понять из источника, что заменитель делает, что pinged YACC...
Конечно, я все еще размышляю здесь, но да, я думаю, мы можем смело предположить, что когда вы вызываете substitute(3 -> y)
, с точки зрения заменяющей функции, выражение всегда было y <- 3
; например функция полностью не осознает, что вы набрали 3 -> y
. do_substitute
, как и 99% функций C, используемых R, обрабатывает только аргументы SEXP
- EXPRSXP
в случае 3 -> y
(== y <- 3
). Это то, о чем я говорил выше, когда я проводил различие между средой R и процессом синтаксического анализа. Я не думаю, что есть что-то, что специально запускает парсер spring в действие, а скорее все, которое вы вводите в интерпретатор, получает синтаксический анализ. Вчера я немного больше читал о генераторе парсера YACC/Bison, и, насколько я понимаю (он не поставил ферму на это), Bison использует указанную вами грамматику (в файле .y
) для создания синтаксического анализатора в C - т.е. функции C, которая выполняет фактический синтаксический анализ ввода. В свою очередь, все, что вы вводите в сеансе R, сначала обрабатывается этой функцией синтаксического анализа C, которая затем делегирует соответствующее действие, которое необходимо предпринять в среде R (я использую этот термин очень слабо, кстати). На этом этапе lhs -> rhs
будет переведена на rhs <- lhs
, **
на ^
и т.д. Например, это выдержка из одной из таблиц примитивные функции в именах .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}},
Вы заметите, что ->
, ->>
и **
здесь не определены. Насколько я знаю, R примитивных выражений, таких как <-
и [
и т.д., Являются самым близким взаимодействием, которое когда-либо имеет среда R с любым базовым кодом C. То, что я предлагаю, заключается в том, что на этом этапе процесса (от ввода набора символов в интерпретатор и нажатия 'Enter', через фактическую оценку действительного выражения R), синтаксический анализатор уже работал над своей магией, поэтому вы не можете получить определение функции для ->
или **
, окружив их обратными окнами, как вы обычно можете.