2017-06-08 4 views
1

Ich versuche, einen Parser mit Nom zu erstellen, der einen Text analysiert, der eine von vielen Optionen sein könnte. Nom hat alt! für, wenn die Werte zur Kompilierungszeit bekannt sind, aber meine Werte werden nicht sein.Wie passt man einen Vektor von Strings mit nom zusammen?

Dies war mein Versuch, meinen eigenen Parser zu erstellen, der eine Vec<String> entgegennehmen kann, und ich bin in ein paar Probleme.

#[macro_use] 
extern crate nom; 

use nom::IResult; 

fn alternative_wrapper<'a>(input: &'a [u8], alternatives: Vec<String>) -> IResult<&'a [u8], &'a [u8]> { 
    for alternative in alternatives { 
     // tag!("alternative"); 
     println!("{}", alternative); 
    } 
    return IResult::Done(input, "test".as_bytes()); 
} 

#[test] 
fn test_date() { 
    let input = "May"; 
    named!(alternative, call!(alternative_wrapper)); 
    let months = vec!(
     "January", 
     "February", 
     "March", 
     "April", 
     "May", 
     "June", 
     "July", 
     "August", 
     "September", 
     "October", 
     "November", 
     "December" 
     ).iter().map(|s| s.to_string()).collect(); 
    println!("{:?}", alternative("May".as_bytes(), months)); 
} 

Ich bin mir bewusst, dass meine alternative_wrapper Funktion tatsächlich tut nichts nützlich, aber das ist nicht das Problem. Dies ist, was Rust beschwert mich für diesen Schnipsel über:

error[E0061]: this function takes 1 parameter but 2 parameters were supplied 
    --> src/parser.rs:32:34 
    | 
17 |  named!(alternative, call!(alternative_wrapper)); 
    |  ------------------------------------------------ defined here 
... 
32 |  println!("{:?}", alternative("May".as_bytes(), months)); 
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 parameter 
    | 
    = note: this error originates in a macro outside of the current crate 

error[E0061]: this function takes 2 parameters but 1 parameter was supplied 
    --> src/parser.rs:17:5 
    | 
6 |/fn alternative_wrapper<'a>(input: &'a [u8], alternatives: Vec<String>) -> IResult<&'a [u8], &'a 
[u8]> { 
7 | |  for alternative in alternatives { 
8 | |   // tag!("alternative"); 
9 | |   println!("{}", alternative); 
10 | |  } 
11 | |  return IResult::Done(input, "test".as_bytes()); 
12 | | } 
    | |_- defined here 
... 
17 |  named!(alternative, call!(alternative_wrapper)); 
    |  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 2 parameters 
    | 
    = note: this error originates in a macro outside of the current crate 

Wie ich einen Parser aus meiner Funktion erstellen kann? Und wie kann ich bestehende Parser wie tag! aus alternative_wrapper verwenden?

Antwort

0

Ich bin nicht wirklich vertraut mit Nom, und immer noch lernen Rust, aber ich habe Parser Kombinator in der Vergangenheit verwendet.

Vorbehalte, es sieht aus wie der named! Makro generiert eine Funktion, die nur einen Parameter, die zu analysierende Zeichenfolge dauert.

Um nom's Erwartungen zu erfüllen, denke ich, ich würde schreiben alternative_wrapper als eine Funktion, die stattdessen eine Funktion zurückgibt. Der Test würde aussehen wie dieses Ende:

#[test] 
fn test_date() { 
    let months = vec!(
     "January", 
     "February", 
     "March", 
     "April", 
     "May", 
     "June", 
     "July", 
     "August", 
     "September", 
     "October", 
     "November", 
     "December" 
     ).iter().map(|s| s.to_string()).collect(); 
    let parser = generate_alternative_parser(months); 
    named!(alternative, call!(parser)); 
    println!("{:?}", alternative("May".as_bytes())); 
} 

Es sieht aus wie Sie einen alt! Ausdruck von tag! s zu konstruieren brauchen würden, aber es ist nicht sofort klar, mich von dem docs, wie Sie das tun würden.

Woher kommt Ihre Liste mit Optionen?

Je nachdem, was genau Sie erreichen möchten, gibt es möglicherweise noch andere Möglichkeiten, um das zu erreichen, was Sie auch erreichen möchten. Beispielsweise können Sie möglicherweise ein beliebiges Wort analysieren und anschließend mit einer Ihrer Optionen überprüfen.

0

Beginnend mit den Fehlern, ist der erste Fehler auf named! nur ein einzelnes Argument, nämlich die Eingabezeichenfolge. named! wird eine Funktion für Sie deklarieren, in diesem Fall mit der Signatur fn(&[u8]) -> IResult<&[u8],&[u8]>. Es gibt keine Magie in Bezug auf andere Argumente, daher wird es nicht funktionieren, wenn Sie versuchen, Ihren Vektor months als zweites Argument zu übergeben. Es gibt eine Variante named! namens named_args!, die verwendet werden kann, um Funktionen mit mehr Argumenten als nur die Eingabe zu deklarieren, die das aussortieren sollte.

Der zweite Fehler ist ähnlich, aber umgekehrt. Sie rufen die alternative_wrapper mit nur dem Eingang und keinem Vektor über call! an. Das Makro call! kann Argumente übergeben, aber Sie müssen es explizit tun, d. H. call!(myparser, monts).

Mit den Gründen für die aussortierten Fehler fragen Sie, wie Sie einen Parser erstellen. Nun, eigentlich ist alternative_wrapperbereits ein Nom-Parser von Signatur, aber da Sie nicht über ein Nom-Makro deklarieren, geschieht keine der magischen Eingabe passieren, weshalb tag! nicht funktioniert in der Funktion Körper, wenn Sie habe es versucht.

Um andere Kombinatoren in einer Funktion zu verwenden, die Sie selbst deklariert haben, müssen Sie die Eingabe manuell an das äußerste Makro übergeben. In diesem Fall ist es nur tag!, aber wenn Sie z. B. do_parse! und dann mehrere Makros verwenden würden, müssten Sie nur die Eingabe an do_parse! übergeben. Ich werde hier eine funktionierende Version mit einigen zusätzlichen Verbesserungen bieten:

#[macro_use] 
extern crate nom; 

use std::str; 
use nom::IResult; 

fn alternative<'a>(input: &'a [u8], alternatives: &Vec<String>) -> IResult<&'a [u8], &'a [u8]> { 
    for alternative in alternatives { 
     match tag!(input, alternative.as_bytes()) { 
      [email protected]::Done(..) => return done, 
      _ =>() // continue 
     } 
    } 
    IResult::Error(nom::ErrorKind::Tag) // nothing found. 
} 

fn main() { 
    let months: Vec<String> = vec![ 
     "January", "February", "March", "April", "May", "June", "July", 
     "August", "September", "October", "November", "December" 
    ].into_iter().map(String::from).collect(); 

    fn print_res(r: IResult<&[u8],&[u8]>) { 
     println!("{:?}", r); 
     println!("{:?}\n", str::from_utf8(r.unwrap().1).unwrap()); 
    } 
    print_res(alternative(b"May", &months)); 
    print_res(alternative(b"August", &months)); 
    print_res(alternative(b"NoGood", &months)); 
} 

Sie es überprüfen können im rust playground aus.

Verwandte Themen