2010-12-04 22 views
6

He estado trabajando en un Lua fslex lexer en mi tiempo libre, usando el manual ocamllex como referencia.Cuerdas largas Lua en fslex

Me golpeó algunos inconvenientes al tratar de tokenizar cadenas largas correctamente. "Cadenas largas" están delimitadas por '[' ('=')* '[' y ']' ('=')* ']' tokens; el número de signos = debe ser el mismo.

En la primera aplicación, el analizador léxico parecía no reconocer patrones [[, produciendo dos LBRACKET fichas a pesar de la regla de partido más largo, mientras que [=[ y las variaciones en donde se reconoce correctamente. Además, la expresión regular no se aseguró de que se utilizara el token de cierre correcto, deteniéndose en la primera captura de ']' ('=')* ']', sin importar el "nivel" de cadena larga real. Además, fslex no parece ser compatible con construcciones "como" en expresiones regulares.

 

let lualongstring = '[' ('=')* '[' (escapeseq | [^ '\\' '[' ])* ']' ('=')* ']' 

(* ... *) 
    | lualongstring { (* ... *) } 
    | '['    { LBRACKET } 
    | ']'    { RBRACKET } 
(* ... *) 

 

que he estado tratando de resolver el problema con otra regla en el léxico:

 

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); 
       (* ... *)   
      } 
 

Pero estoy atascado, por dos razones: en primer lugar, no creo que pueda " push ", por así decirlo, un token a la próxima regla una vez que haya terminado de leer la cadena larga; segundo, no me gusta la idea de leer char por char hasta que se encuentre el token de cierre correcto, haciendo inútil el diseño actual.

¿Cómo puedo tokenar las cuerdas largas de Lua en fslex? Gracias por leer.

+0

Offhand, solo quería mencionar: siempre podría elegir analizarlo, en lugar de leerlo. – Brian

+0

@Brian, ¿pueden explicarlo?:) Estoy un poco perdido tratando de entender cómo analizar una secuencia de tokens no relacionados para crear la cadena larga original, siempre que el lexer pueda producir tokens para todo el contenido de la cadena. Gracias por tu comentario. – Raine

+0

Sí, probablemente no sea una buena estrategia, solo la estaba lanzando. – Brian

Respuesta

5

Disculpas si respondo a mi pregunta, pero me gustaría aportar mi propia solución al problema para futuras consultas.

Estoy manteniendo el estado en las llamadas a la función lexer con la propiedad LexBuffer < _>. BufferLocalStore, que es simplemente una instancia de IDictionary grabable.

Nota: los corchetes largos se utilizan tanto para cadenas largas como para comentarios de líneas múltiples. Esta es a menudo una parte pasada por alto de la gramática de 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." } 

 

Estas son las funciones que utilizo para simplificar el almacenamiento de datos en el 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 

quizás no es muy funcional, pero que parece ser la realización del trabajo.

  • utilizar la regla tokenize hasta el comienzo de un largo soporte se encuentra
  • cambio a la regla longstring y bucle hasta un largo paréntesis de cierre del mismo nivel se encuentra
  • tienda cada lexema que no coincide un corchete largo de cierre del mismo nivel en un StringBuilder, que a su vez se almacena en el BufferLocalStore de LexBuffer.
  • una vez que la cuerda larga ha terminado, borre la BufferLocalStore.

Editar: Puede encontrar el proyecto en http://ironlua.codeplex.com. Lexing y análisis deberían estar bien. Estoy planeando usar el DLR. Comentarios y crítica constructiva bienvenida.

+0

Si eso funciona, entonces acepta la respuesta ;-) –