2011-12-17 8 views
5

Tengo un analizador sintáctico que parece bastante directo. He añadido este sub-programa de análisis hasta el final para dar información acerca de los errores generales de análisis ya que todos los demás sub-programas de análisis fallaron -Cómo resolver el error de FParsec "El combinador 'muchos' se aplicó a un analizador que tiene éxito sin consumir ..."

/// Read the rest of a line as an error. 
let readError = 
    parse { 
     let! restOfLineStr = restOfLine true 
     return makeViolation ("Read error on: " + restOfLineStr + ".") } 

/// Read an expression. 
do readExprRef := 
    choice 
     [attempt readBoolean 
     attempt readCharacter 
     attempt readString 
     attempt readInt 
     attempt readError] // just now added this sub-parser, and get the issue 

Sin embargo, una vez que agrego ReadError como una opción, me sale el error FParsec temido sobre el flujo de consumo en tiempo de ejecución - The combinator 'many' was applied to a parser that succeeds without consuming input and without changing the parser state in any other way. No entiendo por qué obtengo esto ya que uso el resto de la línea analizada para crear una estructura de error usado (aquí 'violación').

¿Alguien me puede ayudar a entender esto? ¿Voy a señalar los errores del analizador al usuario de forma incorrecta? Si no, ¿cómo podría solucionar esto?

¡Gracias por su ayuda!

* * Más detalles

Aquí hay algo más de código que puede ser relevante -

/// The expression structure. 
type Expr = 
| Violation of Expr 
| Boolean of bool 
| Character of char 
| String of string 
| Int of int 

/// Make a violation from a string. 
let makeViolation str = Violation (String str) 

/// Read whitespace character as a string. 
let spaceAsStr = anyOf whitespaceChars |>> fun chr -> string chr 

/// Read a line comment. 
let lineComment = pchar lineCommentChar >>. restOfLine true 

/// Read a multiline comment. 
/// TODO: make multiline comments nest. 
let multilineComment = 
    between 
     (pstring openMultilineCommentStr) 
     (pstring closeMultilineCommentStr) 
     (charsTillString closeMultilineCommentStr false System.Int32.MaxValue) 

/// Read whitespace text. 
let whitespace = lineComment <|> multilineComment <|> spaceAsStr 

/// Skip any white space characters. 
let skipWhitespace = skipMany whitespace 

/// Skip at least one white space character. 
let skipWhitespace1 = skipMany1 whitespace 

/// Read a boolean. 
let readBoolean = 
    parse { 
     do! skipWhitespace 
     let! booleanValue = readStr trueStr <|> readStr falseStr 
     return Boolean (booleanValue = trueStr) } 

/// Read a character. 
let readCharacter = 
    parse { 
     // TODO: enable reading of escaped chars 
     do! skipWhitespace 
     let! chr = between skipSingleQuote skipSingleQuote (manyChars (noneOf "\'")) 
     return Character chr.[0] } 

/// Read a string. 
let readString = 
    parse { 
     // TODO: enable reading of escaped chars 
     do! skipWhitespace 
     let! str = between skipDoubleQuote skipDoubleQuote (manyChars (noneOf "\"")) 
     return String str } 

/// Read an int. 
let readInt = 
    parse { 
     do! skipWhitespace 
     let! value = pint32 
     let! _ = opt (skipString intSuffixStr) 
     do! notFollowedByLetterOrNameChar 
     do! notFollowedByDot 
     return Int value } 

lo sé. Tal vez el problema es que ya está al final de la transmisión una vez que intenta ejecutar el analizador readError. ¿Eso haría que restOfLine no consumiera datos, ni siquiera espacios en blanco?

* Conclusión *

Resulta que el enfoque de error de información con un analizador ReadError que está mal. El enfoque correcto es utilizar un programa de análisis 'hasta el final' como tal -

/// Read the end of input. 
let readEndOfInput = skipWhitespace >>. eof 

// Read multiple exprs. 
let readExprs = many readExpr 

// Read exprs until the end of the input. 
let readExprsTillEnd = readExprs .>> readEndOfInput 

Ahora sólo funcionan readExprsTillEnd cuando necesito para obtener todos los exprs en un flujo de entrada.

Gracias de nuevo, Gustavo!

+0

Se puede publicar más código? Particularmente el cuerpo de la función makeViolation. – Gustavo

+0

Hecho. Por cierto, Gustavo, soy un gran fan de tu blog. Espero que Don Syme esté mirando el trabajo que has hecho: obtener funtores, aplicativos y mónadas sin HK o TC es enorme. Me pregunto si también podemos obtener flechas de alguna forma;) –

+0

Gracias Bryan por sus comentarios sobre mi trabajo con Typeclasses. También espero que las personas del equipo de F # puedan agregar más soporte para esta técnica, de lo contrario dependen del equipo de CLR para implementar el soporte real de Typeclasses en el nivel .NET. En cuanto a las flechas, ¿echó un vistazo a http://stackoverflow.com/questions/4034802/how-would-i-translate-a-haskell-type-class-into-f? Hay más información sobre Arrows en el proyecto en http://code.google.com/p/fsharp-typeclasses. – Gustavo

Respuesta

1

Gracias por el código adicional que ha publicado, lamentablemente no he podido reproducir el error. ¿Pero por qué no intentas eliminar el último attempt? Creo que no tiene sentido y tal vez esté causando un problema.

do readExprRef := 
    choice 
     [attempt readBoolean 
     attempt readCharacter 
     attempt readString 
     attempt readInt 
     readError] 

No soy un experto en FParsec pero creo que el último analizador de una elección no debe ser un intento.

ACTUALIZACIÓN:

El analizador readError tiene éxito incluso sin consumir entrada, si en algún momento tiene una llamada a readExpr como parámetro de un many que nunca termina. me refiero a si se llama

run (many readError) "" ;; 

Usted obtendrá el mensaje de error porque se many seguir aplicando este analizador hasta que falle, pero nunca se producirá un error.

Eche un vistazo a la especificación de función restOfLine en http://www.quanttec.com/fparsec/reference/charparsers.html#members.restOfLine que le advierte sobre esto.

Ahora hay muchas maneras en que puede resolverlo, pero yo diría que tendrá que reconsiderar la forma en que maneja los errores del analizador.

Una cosa que puede hacer es sacar la función readError y luego, cuando se llama a la readExpr analizador se llama de esta manera

let readExprs = many readExpr .>> eof 

hacerlo a hacer cumplir la EF y no se si hay algo que maneja el analizadores sintácticos en la elección antes del eof, FParsec generará automáticamente un mensaje de error agradable para usted.

Y si desea manejar ese error, echar un vistazo a http://www.quanttec.com/fparsec/users-guide/customizing-error-messages.html

+0

Quité el último 'intento' y todavía tiene el mismo comportamiento :(¿Te importaría si te llamara personalmente sobre esto? De no ser así, deslízame tu información de contacto a [email protected] ¡Salud! –

+0

Sí, ¡Esto funciona! ¡He refinado una solución final en la respuesta usando su gran ayuda! ¡Gracias inmensamente! –

Cuestiones relacionadas