Pour être honnête..


h1 5/24/2007 02:31:00 PM

J'ai critiqué ici Python et Javascript.. Je vous ai déja dit que Caml aussi avait quelques comportements décevants, à la limite du bug ?
# let f () =
let s = "abc" in
s.[0] <- char_of_int (1 + int_of_char s.[0]) ;
s ;;
val f : unit -> string = <fun>
# f () ;;
- : string = "bbc"
# f () ;;
- : string = "cbc"

Et grosse dédicace à Romain qui se met à OCaml pour de bon!

Libellés : ,

Sucre


h1 5/18/2007 02:21:00 AM

Déja un mois depuis mon dernier post, il est temps d'en aligner un autre, dans la lignée des notes bien techniques. Le developpement de liquidsoap continue sûrement et plutôt pas lentement. L'API s'étoffe, notamment avec le crossfade intelligent utilisant les intensités relatives des débuts/fins de fichier, et des traitements de son de fou grâce à la libsoundtouch; le format AAC est supporté; nous n'avons presque plus à rougir du support ALSA; la doc devient plus lisible; et une multitude de petits détails s'améliorent. Romain nous pousse à releaser au plus vite, ce qui devrait se faire vu les grands chantiers qui se profilent de nouveau à l'horizon. Pour ma part j'oscille entre l'excitation de voir tout cela si bien tourner, et l'énervement de passer beaucoup de temps à orchestrer ça, corrigeant ou ajoutant du code selon des priorités qui ne sont plus seulement les miennes: tel utilisateur insiste sur un point, tel développeur fait une modification pas forcément judicieuse... et je ne peux m'empêcher de vite intervenir, peut être à tort.

Après la release, ma priorité sera à l'enrichissement du système de type de liquidsoap, avec du polymorphisme et du sous-typage, amenant une simplification de certains opérateurs, et surtout la possibilité de faire cohabiter des sources manipulant différent formats de flux. Après ça, Sam va pouvoir se faire plaisir avec la vidéo... On envisage aussi de généraliser les notions de fade et de crossfade intelligent au delà de la simple mesure de volume: on peut désormais mesurer et changer le BPM d'une source, on se prend à penser à des transitions préservant la continuité du BPM. Voila pour le futur. J'espère parler de typage la prochaine fois, mais pour tout de suite je vous propose encore de l'analyse syntaxique.

Certains sucres syntaxiques semblent tout simples, mais ne rentrent pas dans la sacro-sainte architecture lex/yacc. Par exemple, le preprocessing, qui permet de n'inclure certaines parties du script que quand telle feature est activée: %ifdef FEATURE CODE %endif. Cela ne peut être traité par le parser, ne serait-ce que parce que la construction peut être utilisée à tous les niveaux de la grammaire, ce qui l'alourdirait considérablement. Autre exemple, la notation bien pratique à la Ruby: "Bonjour #{get_name()}!" au lieu de "Bonjour "+get_name(). Ce n'est pas faisable dans le lexer tout seul, et pénible à traiter au niveau du parser, qui en théorie ne dépend pas du lexer.

La solution est d'accepter de s'éloigner un chouilla de l'archi habituelle, en ajoutant des intermédiaires entre analyse syntaxique et lexicale. C'est particulièrement simple en Caml, où un lexer n'est autre qu'une fonction prenant un état (position dans le texte) et renvoyant le symbole suivant dans le flux. On peut donc écrire des fonctions pour enrober le lexer original, afin d'obtenir les notations décrites -- et on simplifie par ailleurs notre lexer en traitant de la même façon les retours à la ligne pour lever les ambiguités dont je parlais la dernières fois. Un petit exemple, le traitement des %ifdef:

(* takes a function of type Lexing.lexbuf->Lang_parser.token,
and returns a function of the same type *)
let preprocess tokenizer =
let state = ref 0 in (* number of nested %ifdef *)
let rec token lexbuf =
match tokenizer lexbuf with
| Lang_parser.PP_IFDEF ->
begin match tokenizer lexbuf with
| Lang_parser.VAR v ->
if feature_enabled v then begin
incr state ;
token lexbuf
end else
let rec skip () =
match tokenizer lexbuf with
| Lang_parser.PP_ENDIF -> token lexbuf
| _ -> skip ()
in
skip ()
| _ -> failwith "expected a variable after %ifdef"
end
| Lang_parser.PP_ENDIF ->
if !state=0 then failwith "no %ifdef to end here" ;
decr state ;
token lexbuf
| x -> x
in
token


Après avoir défini quelques sucres syntaxiques, on utilise tout simplement le lexer étendu expand (preprocess (strip_newlines Lang_lexer.token)) au lieu de Lang_lexer.token. L'exemple complet se trouve sur notre SVN.

Libellés : , , ,