#lang racket ;;; mfcalc-lexer.rkt avec entiers, flottants, booleens et deux fonctions exp et log ;;; Exercice 15.5.5 du chapitre 15 (Analyseurs Syntaxiques) ;;; Livre PCPS : "Premiers Cours de Programmation avec Scheme" ;;; Langage determine par le source (require parser-tools/lex) (require "show.rkt") ; pour les tests (provide get-lexeme value-tokens op-tokens) ; pour le parseur (define-lex-abbrevs ;(blanc (union #\newline #\return #\tab #\space)) (letter (union (char-range "a" "z") (char-range "A" "Z"))) (digit (char-range "0" "9")) (int (repetition 1 +inf.0 digit)) (int* (repetition 0 +inf.0 digit)) (float (union (concatenation int "." int*) (concatenation "." int))) (function (union "log" "exp")) (boolean (union "true" "false")) (var (concatenation letter (repetition 0 +inf.0 (union letter digit))))) (define-tokens value-tokens (NUM BOOL VAR FNCT)) (define-empty-tokens op-tokens (LPAR RPAR + - * / ^ = > < >= <= == != EOF SEMICOLON NEG LOG EXP TRUE FALSE IF THEN ELSE LOWER_THAN_ELSE)) (printf "Generation du mfcalc-lexer... ") (define get-lexeme (lexer ; la fin du fichier d'entree [(eof) 'EOF] ; on invoque recursivement le lexer pour sauter les espaces. La variable input-port est liee dans lexer [(repetition 1 +inf.0 whitespace) (get-lexeme input-port)] ; la variable lexeme est liee dans lexer et represente la chaine reconnue [(union "=" "+" "-" "*" "/" "^" "<" ">" "<=" ">=" "==" "!=" "if" "then" "else") (string->symbol lexeme)] [boolean (token-BOOL (string->symbol lexeme))] ["(" 'LPAR] [")" 'RPAR] [";" 'SEMICOLON] [function (token-FNCT (string->symbol lexeme))] ; une fonction primitive - placee *avant* var [var (token-VAR (string->symbol lexeme))] ; une variable [(union int float) (token-NUM (string->number lexeme))])) ; un entier ou un flottant est un nombre (printf "ok. La fonction (get-lexeme port-in) est disponible.\n") ;;; Pour tester rapidement le lexer, on n'est pas oblige d'ouvrir un fichier-disque, il suffit ;;; d'une chaine de caracteres consideree comme un port d'entree dans lequel on peut lire. Notez ;;; qu'une chaine peut tenir en Scheme sur plusieurs lignes. (define string-test "prix = 2*( 100.5-1); if (x > 100.)==true then .1 else exp(log(x));") (call-with-output-file "test-lex.dat" ; génération du fichier test-lex.dat (lambda (p-out) ; contenant la chaine string-test definie plus haut (fprintf p-out "~a" string-test)) #:exists 'replace) (define (test-lexer) ; on lit cette fois la meme chaine mise dans un fichier (printf "\nTEST : analyse lexicale du fichier \"test-lex.dat\" :\n") (call-with-input-file "test-lex.dat" (lambda (p-in) (do ((i 0 (+ i 1)) (x (get-lexeme p-in) (get-lexeme p-in))) ((equal? x 'EOF) (list i 'lexemes 'ont 'ete 'lus)) (printf "-------> ~a\n" x))))) ;(test-lexer)