2010-02-26 35 views
8

Estoy escribiendo un intérprete de cálculo lambda para la diversión y la práctica. Llegué a iostreams identificadores correctamente no simbólica mediante la adición de una faceta ctype que define puntuacion como espacio en blanco: (! classic_table() probablemente sería más limpio pero que no funciona en OS X)reemplazando ctype <wchar_t>

struct token_ctype : ctype<char> { 
mask t[ table_size ]; 
token_ctype() 
: ctype<char>(t) { 
    for (size_t tx = 0; tx < table_size; ++ tx) { 
    t[tx] = isalnum(tx)? alnum : space; 
    } 
} 
}; 

Y luego intercambiar los faceta en cuando me golpeó un identificador:

locale token_loc(in.getloc(), new token_ctype); 
… 
locale const &oldloc = in.imbue(token_loc); 
in.unget() >> token; 
in.imbue(oldloc); 

No parece ser sorprendentemente poco código cálculo lambda en la web. La mayoría de lo que he encontrado hasta ahora está lleno de caracteres λ unicode. Así que pensé en intentar agregar soporte Unicode.

Pero ctype<wchar_t> funciona de forma completamente diferente a ctype<char>. No hay una tabla maestra; hay cuatro métodos do_is x2, do_scan_is y do_scan_not. Así que hice esto:

struct token_ctype : ctype<wchar_t> { 
typedef ctype<wchar_t> base; 

bool do_is(mask m, char_type c) const { 
    return base::do_is(m,c) 
    || (m&space) && (base::do_is(punct,c) || c == L'λ'); 
} 

const char_type* do_is 
    (const char_type* lo, const char_type* hi, mask* vec) const { 
    base::do_is(lo,hi,vec); 
    for (mask *vp = vec; lo != hi; ++ vp, ++ lo) { 
    if (*vp & punct || *lo == L'λ') *vp |= space; 
    } 
    return hi; 
} 

const char_type *do_scan_is 
    (mask m, const char_type* lo, const char_type* hi) const { 
    if (m & space) m |= punct; 
    hi = do_scan_is(m,lo,hi); 
    if (m & space) hi = find(lo, hi, L'λ'); 
    return hi; 
} 

const char_type *do_scan_not 
    (mask m, const char_type* lo, const char_type* hi) const { 
    if (m & space) { 
    m |= punct; 
    while (* (lo = base::do_scan_not(m,lo,hi)) == L'λ' && lo != hi) 
    ++ lo; 
    return lo; 
    } 
    return base::do_scan_not(m,lo,hi); 
} 
}; 

(Disculpas por el formato plana; la vista previa convierte las pestañas de manera diferente.)

El código es la forma menos elegante. Mejor expresar la noción de que solo la puntuación es espacio en blanco adicional, pero eso hubiera estado bien en el original si hubiera tenido classic_table.

¿Hay una manera más simple de hacer esto? ¿Realmente necesito todas esas sobrecargas? (Las pruebas mostraron do_scan_not es extraño aquí, pero estoy pensando más ampliamente.) ¿Estoy abusando de las facetas en primer lugar? ¿Es lo anterior incluso correcto? ¿Sería mejor estilo implementar menos lógica?

+1

En términos de código de ejemplo, es posible que desee ver Scheme o LISP, ya que ambos idiomas están basados ​​en cálculo lambda. Debería haber algún código LISP o Scheme con el que pueda jugar. –

+0

Todos los lenguajes funcionales estándar son agradables, pero esperaba encontrar algunas fuentes abreviadas de la forma '(\ foo bar. Foo foo \ baz ... ... ad nauseam. Me gustaría jugar con lambda binario cálculo (http://en.wikipedia.org/wiki/Binary_lambda_calculus, http://homepages.cwi.nl/~tromp/cl/cl.html) en lugar de perder tiempo escribiendo código esotérico. Pero esta pregunta es sobre iostreams y disparidad de elegancia entre personajes anchos y estrechos. – Potatoswatter

+0

¿Estás abusando de facetas para construir un analizador de tokenización? Espero también que sea frágil y lento. Por otra parte, es probablemente la forma más directa de aprender el entradas y salidas de iostreams. ¡Gracias por mostrarnos! – sehe

Respuesta

3

(Ha sido un año sin respuesta sustantiva, y he aprendido mucho sobre iostreams en el ínterin ...) La faceta personalizados exclusivamente para servir al operador de extracción cadena in >> token. Ese operador se define en términos de use_facet< ctype<wchar_t> >(in.getloc()).is(ios::space, c) "para el siguiente carácter de entrada disponible c." (§21.3.7.9) ctype::is es simplemente un stub para ctype::do_is, por lo que parece que do_is es suficiente. No obstante, las versiones recientes de la biblioteca estándar de GCC implementan operator>> en términos de scan_is. El problema es que do_scan_is se implementa como una serie de llamadas a do_is, despacho virtual y todo. El archivo de encabezado describe do_scan_is como un gancho para la optimización del usuario.

Por lo tanto, parece que la regla de si-si protege una implementación que solo proporciona la primera anulación.

Tenga en cuenta que la segunda anulación, que recupera valores de máscara, es una excepción. Podría implementarse en términos del primero, construyendo poco a poco la máscara poco a poco.En GCC se implementa en términos de llamadas al sistema, construyendo poco a poco la máscara poco a poco con 15 llamadas por carácter. Esto parece sacrificar el rendimiento y la compatibilidad. Afortunadamente parece que nadie lo usa.


De todos modos, esto es todo muy bien, sino simplemente escribiendo un tokenizer usando streambuf_iterator<wchar_t> es más fácil, mucho más extensible, y simplifica el manejo de excepciones.

+0

Hah. Respondió su propia pregunta. No se dio cuenta de que era una más antigua. Esto es muy informativo. – sehe

2

En mi humilde opinión el código que ha publicado está bien. Podrías implementar algunos de los métodos usando otros si quisieras un código más simple (tal vez a expensas de la eficiencia), pero la forma en que lo hiciste está bien.

La disparidad se basa en el hecho de que las personas no desean tener varias tablas de megabytes en sus programas UNICODE.

existe