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