2013-03-12 23 views
8

Programımın amacı bir dizeden bakmak ve diyalog sorusunu ve cevaplarını çıkarmaktır. Örneğinstring işleme c

: Program dışarı alacaktı ("do you like me?" ("yes" ("we're friends")) ("no" ("i hate you")) )

"Beni seversin?" ve evet veya hayır girmek için seçimler verir. İlgili seçimi seçtiğinizde, "arkadaşız" ya da "senden nefret ediyorum".

Bunun nasıl yapılacağı konusunda herhangi bir kitaplık veya çözüm var mı?

+5

Ne denediniz? http://mattgemmell.com/2008/12/08/ne-denediniz/ – kay

+2

Kay, güzel bir URL aldınız! Onu kaydedeceğim :) – troyane

+3

@troyane (ve ayrıca @Kay) Bir yazar bir yazar için çok güzeldi: http://whathaveyoutried.com – iamnotmaynard

cevap

58

Yanılıyorsam düzeltin, ancak Lisp çözümleyicisi işi oldukça iyi yapardı. : C Cidden, bu, parantezin veya diğer parantez içi ifadelerin güzel parantezlenmiş listelerine benziyor. Basit bir özyinelemeli çözümleyici yeterlidir, sadece ihtiyaçlarınızı karşılayan ayrıştırma ağacı olarak oluşturulacak bir veri yapısını icat edin.

Düzenleme: Kahretsin, sonunda Ha, o, itiraf etmeliyim doğru 10pm ve 12 arasında daha da çok basit bir ayrıştırıcı tahrik eden oldukça önemsiz bir görev değil ... bunu başardı.

/* 
* lrparser.c 
* LR-parser 
* A recursive Lisp-subset parser 
* that has a misleading name (it's not an LALR, but a recursive descent one). 
* 
* Originally written to answer 
* http://stackoverflow.com/questions/15371008/string-processing-in-c/ 
* 
* Made in some *really* bored hours by Árpád Goreity (H2CO3) 
* on 12-03-2013 
* 
* Language: C99 (not sure if POSIX) 
*/ 

#include <stdlib.h> 
#include <ctype.h> 
#include <string.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <assert.h> 
#include <stdarg.h> 
#include <stdbool.h> 

// AST node type 
enum { 
    NODE_STRING, 
    NODE_LIST 
}; 

// Permitted tokens 
enum { 
    TOKEN_INVALID = -1, 
    TOKEN_LPAREN = 0, 
    TOKEN_RPAREN, 
    TOKEN_STRING, 
    TOKEN_END 
}; 

// Useful for debugging and error reporting 
static const char *toknames[] = { 
    "Left paren", 
    "Right paren", 
    "String", 
    "End" 
}; 

// ...Or simply an AST node... 
struct ParseTree { 
    int type; // string or list 
    char *string; // if any 
    struct ParseTree **children; 
    size_t n_children; 
}; 

// Construct a node structure from a type and any necessary data 
static struct ParseTree *node_new(int type, ...) 
{ 
    va_list args; 
    va_start(args, type); 
    struct ParseTree *node = malloc(sizeof(*node)); 
    assert(node != NULL); 

    node->type = type; 
    if (type == NODE_STRING) { 
     /* If the node is a string, fill it 
     * (ownership transfer: the argument will be 
     * free()'d by the node_free() function) 
     */ 
     node->string = va_arg(args, char *); 
    } 

    node->children = NULL; 
    node->n_children = 0; 

    va_end(args); 

    return node; 
} 

void node_free(struct ParseTree *tree) 
{ 
    switch (tree->type) { 
    case NODE_STRING: 
     free(tree->string); 
     break; 
    case NODE_LIST: 
     for (int i = 0; i < tree->n_children; i++) { 
      node_free(tree->children[i]); 
     } 
     free(tree->children); 
     break; 
    default: 
     fprintf(stderr, "Warning: unknown node type %d\n", tree->type); 
     break; 
    } 

    free(tree); 
} 

// Sorry, the usual logarithmic storage expansion is omitted for clarity 
void node_add(struct ParseTree *parent, struct ParseTree *child) 
{ 
    assert(parent != NULL); 
    assert(child != NULL); 

    parent->n_children++; 
    parent->children = realloc(parent->children, sizeof(parent->children[0]) * parent->n_children); 
    // Lazy error checking: assert() instead of compare to NULL 
    assert(parent->children != NULL); 
    parent->children[parent->n_children - 1] = child; 
} 

// Just in order to break thread safety 
static const char *s = NULL; // the string to be parsed 
static char *curstr = NULL; // the contents of the string value of the current token 
static int curtok; // the current token 

// The tokenizer 
static int lex() 
{ 
    // Whitespace doesn't matter 
    while (isspace(s[0])) { 
     s++; 
    } 

    // end of string 
    if (s[0] == 0) { 
     return TOKEN_END; 
    } 

    // The followin four are obvious 
    if (s[0] == '(') { 
     s++; 
     return curtok = TOKEN_LPAREN; 
    } 

    if (s[0] == ')') { 
     s++; 
     return curtok = TOKEN_RPAREN; 
    } 

    if (s[0] == '"') { 
     const char *begin = s; 
     while (*++s != '"') 
      ; 

     size_t sz = s - begin - 2 + 1; 
     curstr = malloc(sz + 1); 
     memcpy(curstr, begin + 1, sz); 
     curstr[sz] = 0; 

     // skip trailing quotation mark (") 
     s++; 
     return curtok = TOKEN_STRING; 
    } 

    return curtok = TOKEN_INVALID; 
} 

void expect(int tok) 
{ 
    if (curtok != tok) { 
     fprintf(stderr, "Error: expected token %s, got %s\n", toknames[tok], toknames[curtok]); 
     abort(); 
    } 

    lex(); 
} 

// a. k. a. "parse()" 
// Simple recursive (one-level...) descent (root == list) approach 
static struct ParseTree *recurse_and_descend() 
{ 
    expect(TOKEN_LPAREN);  

    struct ParseTree *node = node_new(NODE_LIST); 

    struct ParseTree *child; 
    while (curtok != TOKEN_RPAREN) { 
     if (curtok == TOKEN_LPAREN) { 
      child = recurse_and_descend(); 
     } else if (curtok == TOKEN_STRING) { 
      child = node_new(NODE_STRING, curstr); 
      lex(); 
     } else { 
      fprintf(stderr, "Unexpected token '%s'\n", toknames[curtok]); 
      // lazy programmer's safety system, let the kernel do the dirty work 
      abort(); 
     } 
     node_add(node, child); 
    } 

    expect(TOKEN_RPAREN); 

    return node; 
} 

static struct ParseTree *parse(const char *str) 
{ 
    s = str; // poor man's initialization 
    lex(); // The first breath of fresh token makes the baby's heart beat 
    return recurse_and_descend(); // Let's do the Harlem shake! 
} 

// petite helper function 
static void dump_indent(int indent) 
{ 
    for (int i = 0; i < indent; i++) { 
     printf("\t"); 
    } 
} 

// Because 0x7f502a00 is not very meaningful for the human eye 
static void dump_tree(struct ParseTree *tree, int indent) 
{ 
    dump_indent(indent); 

    switch (tree->type) { 
    case NODE_STRING: 
     printf("<String \"%s\">\n", tree->string); 
     break; 
    case NODE_LIST: 
     printf("<List>\n"); 
     for (int i = 0; i < tree->n_children; i++) { 
      dump_tree(tree->children[i], indent + 1); 
     } 
     break; 
    default: 
     printf("Unknown node\n"); 
     break; 
    } 
} 

int main(int argc, char *argv[]) 
{ 
    struct ParseTree *tree = parse(argv[1]); 
    dump_tree(tree, 0); 
    node_free(tree); 

    return 0; 
} 

Kullanımı: ama hileci bir sürü işe, sağlam değildir

İsterseniz
h2co3-macbook:~ h2co3$ ./lrparser "(\"do you like me?\" (\"yes\" (\"we're friends\")) (\"no\" (\"i hate you\" \"me too\")))" 
<List> 
    <String "do you like me?"> 
    <List> 
     <String "yes"> 
     <List> 
      <String "we're friends"> 
    <List> 
     <String "no"> 
     <List> 
      <String "i hate you"> 
      <String "me too"> 
+2

@EdS. Teşekkürler: D Ama ciddi, bu Common Lisp'in uygun bir alt kümesine benzemiyor mu? –

+0

Gerçekten öyle. –

+0

Haklısınız, ancak C'deki bir problemi çözmek için Lisp'e ulaşmak bir çeşit güzel, zarif, işlevsel, meta kapsam sürünürdür. –

7

"çalışır bir şey". Eğer gerçekten çalışmasını istiyorsanız, LALR(1) parsers'da biraz çalışmanız ve sonra kendi ayrıştırıcınızı yuvarlamak için yeterli olup olmadığına (ya da YACC gibi bir şey kullanmak istiyorsanız) karar vermeniz gerekir.

Bunun Bağlam Serbest Gramer Sonra yukarıdaki dil kombinasyonları işleme değişikliklere neden olabilir hangi analiz

QUESTION => '(' '"' TEXT '"' RESPONSES ')' 
RESPONSES => null | RESPONSE RESPONSES 
RESPONSE => '(' '"' TEXT '(' '"' TEXT '"' ')' ')' 
TEXT => all characters except '(' '"' ')' 

benzemeye görünüyor. Temel olarak SORUNLAR hiçbir şey ya da bir '(' ile başlayan bir şeye yeniden başlayabilir, bu demektir ki bu işlem sırasında o noktada lookahead (lookahead değil mi görerek) yeni bir SORUNU ya da SORU sonunu ayrıştırma arasındaki farkı anlayabilirsiniz. henüz ayrıştırılan karakter) '(' veya ')' dir.

Bir modda ayrıştırma işlemi oldukça basittir.Eğer karakter düzeltilmişse, beklenenin eşleştiğini kontrol edin ve ayrıştırılmış öğelerin dizinini ilerletin. karakter sabitlenmez (metinde olduğu gibi), sınırlar olup olmadığını kontrol etmek için bir rutini kullanır ve beklenenin dışında herhangi bir şey ayrıştırıcıyı hata durumuna sokmalıdır.

+0

Bu elbette "yapmanın yolu" olduğu için, C'nin yeni başlayanlara dize ayrıştırma dünyasını anlamalarına yardımcı olamayacağını düşünüyorum. :/ –

+0

@DR Eh, hepimiz bir yerde başlamalıyız. En basit cevaplar zaten işe yarayan hack'leri kapsamakta, ancak temel unsurlardan hiç bahsetmemektedir. –