2016-06-01 2 views
1

Я пишу интерфейс для языка (по ocamllex и ocamlyacc).Верно обрабатывать белый интервал в симпатичном принтере

Таким образом, frond-end может построить Abstract Syntax Tree (AST) из программы. Затем мы часто пишем красивый принтер, который берет AST и печатает программу. Если позже мы просто хотим скомпилировать или проанализировать AST, большую часть времени нам не нужно, чтобы печатная программа была точно так же, как и исходная программа с точки зрения белого интервала. Однако на этот раз я хочу написать красивый принтер, который печатает точно той же программой, что и оригинальная, с точки зрения белого интервала.

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

Например, это, как я в настоящее время иметь дело с (то есть, пропускаемого) белого интервала в lexer.mll:

rule token = parse 
    ... 
    | [' ' '\t']  { token lexbuf }  (* skip blanks *) 
    | eof    { EOF } 

Кто-нибудь знает, как изменить это, а также другие части переднего конца к правильно ли учитывать пробелы во время последующей печати?

+0

Если ваш симпатичный принтер никоим образом не изменяет пробелы, что именно он делает, что оправдывает слово «довольно»? :) Другими словами, почему бы вам просто не отрегулировать весь текст ввода? – rici

+0

Я вижу ... это потому, что для некоторых частей программы я не хочу менять белые пробелы. Например, для вызова функции 'f (arg0, arg1, arg2, arg3)', я хочу сохранить как есть, вместо того, чтобы изменять его до довольно 'f (arg0, arg1, arg2, arg3)'. – SoftTimur

ответ

1

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

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

Зубр имеет некоторые особенности, которые упрощают бухгалтерскую работу по запоминанию объектов местоположения; возможно, что ocamlyacc включает аналогичные функции, но я ничего не видел в документации. В любом случае, прямолинейно поддерживать объект местоположения, связанный с каждым входным токеном.

С этой информацией легко воссоздать пробел между двумя соседними маркерами, если разделение жетонов было пропущенным. Комментарии - еще одна проблема.

Это решение, которое проще, чем просто прикрепление предшествующих пробелов (и даже комментариев) к каждому токену, поскольку оно лексируется.

+0

OCaml имеет тип [позиция] (http://caml.inria.fr/pub/docs/manual-ocaml/libref/Lexing.html), чтобы получить позицию токена. Не могли бы вы рассказать подробнее о том, как ** поддерживать ** местоположение, связанное с каждым токеном? Должен ли я хранить местоположение или количество белых пробелов для каждого элемента в AST, чтобы использовать эту информацию в принтере? – SoftTimur

+0

@softtimur: я бы сохранил информацию о местоположении как часть токена. Но, вероятно, есть и другие альтернативы. Дополнение маркера просто – rici

+0

Извините, что вы подразумеваете под «сохранить информацию о местоположении как часть токена»? Как выглядят типы? – SoftTimur

0

У вас могут быть утверждения соответствия, которые печатают различное количество пробелов в зависимости от маркера, с которым вы имеете дело. Обычно я печатаю 1 пробел, если токен равен: id, num, define statement, assign (=)

Если токен является арифметическим выражением, я бы напечатал одно пробел перед и одним пробелом после него.

Если вы имеете дело с утверждением if или while, я бы отделил тело четырьмя пробелами.

Я думаю, что лучше всего было бы написать функцию pretty_print, такие как:

let rec pretty_print pos ast = 
    match ast with 
    |Some_token -> String.make pos ' '; (* adds 'pos' number of spaces; pos will start off as zero. *) 
        print_string "Some_token"; 
    |Other_token... 

В общем, я бы обрабатывать пробелы путем сопоставления каждый маркер индивидуально в рекурсивной функции, и распечатки соответствующего количества пространства.

+1

Я предполагаю, что это не воссоздало бы формат оригинальной программы, но создало бы совершенно отступовую программу –

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