Lua длинные строки в fslex
я работал над лексером Lua fslex в свободное время, используя руководство ocamllex в качестве ссылки.
Я ударил несколько коряг, пытаясь правильно обозначить длинные строки. "Длинные строки" ограничиваются '[' ('=')* '['
и ']' ('=')* ']'
жетоны, количество =
знаки должны быть одинаковыми.
в первой реализации, лексер, казалось, не признать [[
моделей, создав два LBRACKET
токены, несмотря на самое длинное правило матча, тогда как [=[
и вариации где распознается правильно. Кроме того, регулярное выражение не смогло гарантировать, что используется правильный токен закрытия, остановившись на первом ']' ('=')* ']'
захват, независимо от фактической длинной строки "уровень". Кроме того, fslex, похоже, не поддерживает конструкции "as" в регулярных выражениях.
let lualongstring = '[' ('=')* '[' ( escapeseq | [^ '' '[' ] )* ']' ('=')* ']'
(* ... *)
| lualongstring { (* ... *) }
| '[' { LBRACKET }
| ']' { RBRACKET }
(* ... *)
Я пытался решить проблему с другим правилом в лексере:
rule tokenize = parse
(* ... *)
| '[' ('=')* '[' { longstring (getLongStringLevel(lexeme lexbuf)) lexbuf }
(* ... *)
and longstring level = parse
| ']' ('=')* ']' { (* check level, do something *) }
| _ { (* aggregate other chars *) }
(* or *)
| _ {
let c = lexbuf.LexerChar(0);
(* ... *)
}
но я застрял, по двум причинам: Во-первых, я не думаю, что я могу "давить", так сказать, маркер следующее правило, как только я закончу читать длинную строку; во-вторых, мне не нравится идея чтения char за char, пока не будет найден правильный токен закрытия, что делает текущий дизайн бесполезным.
как я могу токенизировать длинные строки Lua в fslex? Спасибо за чтение.
1 ответов
извинения, если я отвечу на свой вопрос, но я хотел бы внести свой вклад в мое собственное решение проблемы для дальнейшего использования.
Я сохраняю состояние через вызовы функций lexer с помощью LexBuffer<_>.Свойство BufferLocalStore, которое является просто записываемым экземпляром IDictionary.
Примечание: длинные скобки используются как длинными строковыми, так и многострочными комментариями. Это часто упускается из виду часть грамматики языка.
let beginlongbracket = '[' ('=')* '['
let endlongbracket = ']' ('=')* ']'
rule tokenize = parse
| beginlongbracket
{ longstring (longBracketLevel(lexeme lexbuf)) lexbuf }
(* ... *)
and longstring level = parse
| endlongbracket
{ if longBracketLevel(lexeme lexbuf) = level then
LUASTRING(endLongString(lexbuf))
else
longstring level lexbuf
}
| _
{ toLongString lexbuf (lexeme lexbuf); longstring level lexbuf }
| eof
{ failwith "Unexpected end of file in string." }
вот функции, которые я использую для упрощение хранения данных в BufferLocalStore:
let longBracketLevel (str : string) =
str.Count(fun c -> c = '=')
let createLongStringStorage (lexbuf : LexBuffer<_>) =
let sb = new StringBuilder(1000)
lexbuf.BufferLocalStore.["longstring"] <- box sb
sb
let toLongString (lexbuf : LexBuffer<_>) (c : string) =
let hasString, sb = lexbuf.BufferLocalStore.TryGetValue("longstring")
let storage = if hasString then (sb :?> StringBuilder) else (createLongStringStorage lexbuf)
storage.Append(c.[0]) |> ignore
let endLongString (lexbuf : LexBuffer<_>) : string =
let hasString, sb = lexbuf.BufferLocalStore.TryGetValue("longstring")
let ret = if not hasString then "" else (sb :?> StringBuilder).ToString()
lexbuf.BufferLocalStore.Remove("longstring") |> ignore
ret
возможно, это не очень функционально, но, похоже, он выполняет свою работу.
- использовать правила разметки до начала длинный кронштейн нашел
- перейти к longstring правила и петли до закрытия длинных кронштейна на этом же уровне находится
- хранить каждую лексему, которая не соответствует закрывающей длинной скобке того же уровня, в StringBuilder, который, в свою очередь хранится в буфере LexBuffer BufferLocalStore.
- как только длинная строка закончится, очистите BufferLocalStore.
Edit: вы можете найти проект в http://ironlua.codeplex.com. Лексика и синтаксический анализ должны быть в порядке. Я планирую использовать DLR. Комментарии и конструктивная критика приветствуются.