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. Комментарии и конструктивная критика приветствуются.