Как интерпретатор Ruby анализирует строки с двойными кавычками

Фон:

Я реализую язык, похожий на Ruby, под названием Sapphire, чтобы опробовать некоторые идеи, которые у меня есть параллелизм в языках программирования. Я пытаюсь скопировать строки Ruby в двойные кавычки со встроенным кодом, который я считаю очень полезным как программист.

Вопрос:

Как любой из интерпретаторов Ruby превращает строку в двойных кавычках со встроенным кодом в AST?

eg:

puts "The value of foo is #{@foo}."

puts "this is an example of unmatched braces in code: #{ foo.go('}') }"

Подробности:

У меня проблема в том, как решить, какой } закрывает блок кода. Блоки кода могут иметь в себе другие фигурные скобки, и, приложив немного усилий, их можно не сравнить. Лексер может найти начало блока кода в строке, но без помощи анализатора он не может точно знать, какой символ является концом этого блока.

Похоже, что parse.y файл Ruby выполняет шаги лексирования и синтаксического анализа, но при чтении это кошмар это 11628 строк без комментариев и большого количества сокращений.


person John F. Miller    schedule 30.01.2014    source источник
comment
Кроме того, строковые литералы должны решать аналогичную сложную задачу: %Q{{hi}} #=> "{hi}"   -  person Alex Wayne    schedule 31.01.2014
comment
Алекс, ваш пример выдаст ошибку, если вы введете его в IRB. Ruby завершит строку на первой закрывающей скобке. Поэтому вы можете выбрать свой символ в этой конструкции. т.е.% Q | {hi} | Символ, открывающий конструкцию, - это символ, который ее закрывает. Исключение составляют {[(‹которые закрываются›)]}.   -  person John F. Miller    schedule 31.01.2014
comment
Майкл: heredocs - это просто. Открывающий символ не может встречаться в начале строки, за исключением того, что он закрывает heredoc. Если вы найдете завершающий символ, он ДОЛЖЕН быть концом.   -  person John F. Miller    schedule 31.01.2014
comment
В pry (другой ответ) он не показывает никаких ошибок и не должен. Я изложил эту суть, поскольку не могу легко ее объяснить: gist.github.com/nedzadarek/8744476 Проверено на pry на Ruby версии 1.9.3 и 2.0   -  person Darek Nędza    schedule 01.02.2014
comment
Что касается heredocs, закрывающий символ должен появляться один в новой строке без каких-либо других символов. Другие случаи появления символа допустимы. Таким образом, при рассмотрении ST символ ST (см. Пробел после ST) не закрывает heredoc. 2. Существует специальный синтаксис: var1=<<-ST, который позволяет вам ставить пробелы перед завершающей строкой (допустимо `ST`). 3. Heredocs может встречаться несколько раз, и a,b=<<S,<<S является допустимой первой строкой heredocs.   -  person Darek Nędza    schedule 01.02.2014
comment
К вашему сведению: уже существует два языка, вдохновленных Ruby, с названием Sapphire.   -  person Jörg W Mittag    schedule 03.02.2014


Ответы (5)


Правда, файлы Yacc поначалу могут быть немного сложными для чтения, а parse.y - не лучший файл для начать с. Вы ознакомились с различными правилами производства струн? У вас есть какие-то конкретные вопросы?

Что касается фактического синтаксического анализа, то действительно нередко лексеры также анализируют числовые литералы и строки, см., Например, принятый ответ на аналогичный вопрос здесь на SO. Если вы подойдете к этому так, не так уж сложно понять, как это сделать. Нажатие #{ внутри строки в основном запускает новый контекст синтаксического анализа, который снова анализируется как выражение. Это означает, что первый } в вашем примере не может быть завершающим для интерполяции, поскольку он является частью литеральной строки в выражении. Как только вы дойдете до конца выражения (помните о разделителях выражений, таких как ;), вам понадобится следующий }.

person Michael Kohl    schedule 30.01.2014

Это не полный ответ, но я оставляю его в надежде, что он может быть полезен либо мне, либо тем, кто следит за мной.

Матц дает довольно подробное изложение yylex() функции parse.y в главе 11 своей книги. Он не упоминает напрямую строки, но описывает, как лексер использует lex_state для разрешения нескольких локально неоднозначных конструкций в Ruby.

Воспроизведение английского перевода этой главы можно найти на здесь.

person John F. Miller    schedule 31.01.2014

Имейте в виду, что они не обязаны (создавать AST во время компиляции).

Строки Ruby могут быть собраны во время выполнения и будут правильно интерполировать. Следовательно, все механизмы синтаксического анализа и оценки должны быть доступны во время выполнения. Любая работа, проделанная во время компиляции в этом смысле, может считаться оптимизацией.

Так почему это важно? Потому что существуют очень эффективные стековые методы анализа и оценки выражений, которые не создают и не украшают AST. Строка читается (анализируется) слева направо, и при обнаружении встроенных токенов они либо оцениваются, либо помещаются в стек, либо вызывают выталкивание и оценку содержимого стека.

Это простой метод для реализации при условии, что выражения относительно просты. Если вы действительно хотите использовать всю мощь языка внутри каждой строки, вам понадобится полный компилятор во время выполнения. Не все так делают.

Раскрытие информации: я написал коммерческий языковой продукт, который делает именно это.

person david.pfx    schedule 28.02.2014

Dart также поддерживает выражения, вставляемые в строки, такие как Ruby, и я просмотрел несколько парсеров для этого. Я считаю, что они определяют отдельные токены для строкового литерала, предшествующего интерполяции, и строкового литерала в конце. Итак, если вы токенизируете:

"before ${the + expression} after"

Вы получите такие токены, как:

STRING_START "before "
IDENTIFIER   the
PLUS
IDENTIFIER   expression
STRING       " after"

Затем в вашем синтаксическом анализаторе это довольно простой процесс обработки STRING_START для синтаксического анализа интерполированных выражений, следующих за ним.

person munificent    schedule 01.02.2014

Наш синтаксический анализатор Ruby (см. Мою биографию) обрабатывает «строки» Ruby как сложные объекты, имеющие множество подструктур, включая токены начала и конца строки, простые фрагменты строковых литералов, множество забавных последовательностей пунктуации, представляющих различные операторы регулярного выражения, и, конечно же, рекурсивно, большая часть самого Ruby для выражений, вложенных в такие строки.

Это достигается за счет того, что лексер может обнаруживать и генерировать такие фрагменты строки в (для Ruby, многих) специальных режимах лексирования. У парсера есть (под) грамматика, которая определяет допустимые последовательности токенов. И такой синтаксический анализ решает исходную проблему OP; синтаксический анализатор знает, соответствует ли фигурная скобка другим фигурным скобкам из содержимого регулярного выражения и / или является ли регулярное выражение полностью собранным, а фигурная скобка соответствует концу блока.

Да, он строит AST кода Ruby и регулярных выражений.

Цель всего этого - позволить нам создавать анализаторы и преобразователи кода Ruby. См. https://softwarerecs.stackexchange.com/q/11779/101.

person Ira Baxter    schedule 04.09.2016