Как анализировать комментарии в стиле C с помощью Alex lexer?

NB. Я использую этот шаблон Алекса от Саймона Марлоу.

Я хотел бы создать лексер для комментариев в стиле C. Мой текущий подход создает отдельные токены для начальных, конечных, средних и однострочных комментариев.

%wrapper "monad"

tokens :-
  <0> $white+ ;
  <0> "/*"               { mkL LCommentStart `andBegin` comment }
  <comment> .            { mkL LComment }
  <comment> "*/"         { mkL LCommentEnd `andBegin` 0 }
  <0> "//" .*$           { mkL LSingleLineComment }

data LexemeClass
  = LEOF
  | LCommentStart
  | LComment
  | LCommentEnd
  | LSingleLineComment
  • Как я могу уменьшить количество средних токенов? За ввод /*blabla*/ я получу 8 жетонов вместо одного!
  • Как я могу удалить часть // из токена однострочного комментария?
  • Можно ли лексировать комментарии без обертки monad?

person danbst    schedule 12.07.2014    source источник


Ответы (1)


Посмотри на это:

http://lpaste.net/107377

Протестируйте что-то вроде:

echo "This /* is a */ test" | ./c_comment

который должен печатать:

Right [W "This",CommentStart,CommentBody " is a ",CommentEnd,W "test"]

Ключевые подпрограммы alex, которые вам нужно использовать:

alexGetInput -- gets the current input state
alexSetInput -- sets the current input state
alexGetByte  -- returns the next byte and input state
andBegin     -- return a token and set the current start code

Каждая из подпрограмм commentBegin, commentEnd и commentBody имеет следующую подпись:

AlexInput -> Int -> Alex Lexeme

где Lexeme обозначает тип вашего токена. Параметр AlexInput имеет вид (для обёртки монады):

(AlexPosn, Char, [Bytes], String)

Параметр Int представляет собой длину совпадения, хранящуюся в поле String. Поэтому форма большинства обработчиков токенов будет следующей:

handler :: AlexInput -> Int -> Alex Lexeme
handler (pos,_,_,inp) len = ... do something with (take len inp) and pos ...

В целом кажется, что обработчик может игнорировать поля Char и [Bytes].

Обработчики commentBegin и commentEnd могут игнорировать как аргументы AlexInput, так и Int, потому что они просто соответствуют строкам фиксированной длины.

Обработчик commentBody работает, вызывая alexGetByte для накопления тела комментария до тех пор, пока не будет найдено "*/". Насколько я знаю, комментарии C не могут быть вложенными, поэтому комментарий заканчивается при первом появлении «*/».

Обратите внимание, что первый символ тела комментария находится в переменной match0. На самом деле, в моем коде есть ошибка, так как он не будет правильно соответствовать "/**/". Он должен смотреть на match0, чтобы решить, начинать ли с loop или loopStar.

Вы можете использовать ту же технику для разбора комментариев в стиле "//" или любого токена, где требуется нежадное совпадение.

Еще один ключевой момент заключается в том, что такие шаблоны, как $white+, обозначаются начальным кодом:

<0>$white+

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

Вы можете использовать другую оболочку, но обратите внимание, что структура типа AlexInput может быть другой, например. для базовой оболочки это просто 3-кортеж: (Char,[Byte],String). Просто посмотрите на определение AlexInput в сгенерированном файле .hs.

Последнее замечание... накапливать символы с помощью ++, конечно, довольно неэффективно. Вероятно, вы захотите использовать Text (или ByteString) для аккумулятора.

person ErikR    schedule 12.07.2014
comment
Здорово! Я подумал, что нужно попытаться написать больше правил, менее явное лексирование. Но в поисках подхода на основе правил я нашел только /* (([^*])|(*+[^\/*]))*** */ { mkL LComment}, что должно быть менее эффективным, чем ваше решение - person danbst; 12.07.2014
comment
Да, если вы можете найти жадное регулярное выражение, вы можете его использовать. Но если, например, ваши комментарии могут быть вложенными, кажется, вам придется создавать свои собственные, используя alexGetByte и друзей. - person ErikR; 12.07.2014
comment
Ссылка не работает - person Poscat; 24.03.2020