Lua длинные струны в fslex - PullRequest
       54

Lua длинные струны в fslex

6 голосов
/ 04 декабря 2010

В свободное время я работал над лексером Lua fslex, используя руководство ocamllex в качестве справки.

Я ударил несколько препятствий, пытаясь правильно разметить длинные строки. «Длинные строки» ограничены токенами '[' ('=')* '[' и ']' ('=')* ']'; количество знаков = должно быть одинаковым.

В первой реализации лексер, казалось, не распознавал шаблоны [[, производя два токена LBRACKET, несмотря на правило самого длинного соответствия, тогда как [=[ и вариации распознавались правильно. Кроме того, регулярное выражение не гарантировало использование правильного закрывающего токена, останавливаясь на первом захвате ']' ('=')* ']', независимо от фактической длинной строки "level". Кроме того, 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 Ответ

5 голосов
/ 05 декабря 2010

Прошу прощения, если я отвечу на свой вопрос, но я бы хотел поделиться своим собственным решением проблемы для дальнейшего использования.

Я сохраняю состояние при вызове функций лексера с помощью свойства LexBuffer <_>. BufferLocalStore, которое является просто записываемым экземпляром IDictionary.

Примечание: длинные скобки используются как в длинных строковых, так и в многострочных комментариях. Это часто пропускаемая часть грамматики Lua.



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

Возможно, это не очень функционально, но похоже, что работа сделана.

  • использовать правило токенизации, пока не будет найдено начало длинной скобки
  • переключиться на правило длинной строки и выполнять цикл, пока не будет найдена закрывающая длинная скобка того же уровня
  • хранить каждую лексему, которая не соответствует закрывающей длинной скобке того же уровня, в StringBuilder, который, в свою очередь, сохраняется в LexBuffer BufferLocalStore.
  • когда длинная строка закончится, очистите BufferLocalStore.

Редактировать: Вы можете найти проект на http://ironlua.codeplex.com. Лексирование и анализ должны быть в порядке. Я планирую использовать DLR. Комментарии и конструктивная критика приветствуются.

...