2014-01-30 3 views
4

Фоновые:Как рубин interperter разбора строк в двойных кавычках

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

Вопрос:

Как любой из переводчиков Руби превратить строку в двойные кавычки с внедренным кодом в и АСТ?

например:

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

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

Детали:

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

Похоже, что файл parse.y Ruby делает как шаги лексирования, так и разбора, но reading that thing is a nightmare - это 11628 строк длиной без комментариев и много аббревиатуры.

+0

Кроме того, FWIW, строковые литералы должны решить подобную хитрую задачу: '% Q {{привет}} # => "{} привет"' –

+0

Как сделать heredocs ... –

+0

Алекс, ваш пример даст если вы ввели его в IRB. Ruby закончит строку на первой закрывающей скобке. Вот почему вы можете выбрать свой символ в этой конструкции. т.е.% Q | {hi} | Символом, который открывает конструкцию, является тот, который ее закрывает. Исключениями являются {[(<, которые закрываются на>)]}. –

ответ

2

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

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

1

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

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

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

1

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

"before ${the + expression} after" 

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

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

Затем в анализаторе это довольно простой процесс обработки STRING_START разобрать интерполированное выражение (ы) после его.

1

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

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

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

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

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

0

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

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

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

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

Смежные вопросы