Cleaned up the source.
Showing
1 changed file
with
109 additions
and
123 deletions
1 | #! %BINDIR%/guimb --source | 1 | #! %GUILE_BINDIR%/guile -s |
2 | # Emacs, its -*- scheme -*- | 2 | # Emacs, its -*- scheme -*- |
3 | !# | 3 | !# |
4 | ;;;; GNU mailutils - a suite of utilities for electronic mail | 4 | ;;;; GNU mailutils - a suite of utilities for electronic mail |
... | @@ -21,19 +21,21 @@ | ... | @@ -21,19 +21,21 @@ |
21 | 21 | ||
22 | ;;;; This is a Sieve to Scheme translator. | 22 | ;;;; This is a Sieve to Scheme translator. |
23 | ;;;; | 23 | ;;;; |
24 | ;;;; To convert a sieve script into equivalent Scheme program, executable | 24 | ;;;; To convert a sieve script into equivalent Scheme program, run: |
25 | ;;;; by guimb, run: | ||
26 | ;;;; | 25 | ;;;; |
27 | ;;;; guile -s sieve.scm --file <sieve-script-name> --output <output-file-name> | 26 | ;;;; guile -s sieve.scm --file <sieve-script-name> --output <output-file-name> |
28 | ;;;; | 27 | ;;;; |
29 | ;;;; To compile and execute a sieve script upon a mailbox, run: | 28 | ;;;; To compile and execute a sieve script upon a mailbox, run: |
30 | ;;;; | 29 | ;;;; |
31 | ;;;; guimb -f sieve.scm -{ --file <sieve-script-name> -} --mailbox ~/mbox | 30 | ;;;; guile -s sieve.scm --file <sieve-script-name> [mailbox-name] |
31 | ;;;; or | ||
32 | ;;;; guimb [--mailbox mailbox-name] -s sieve.scm --file <sieve-script-name> | ||
32 | 33 | ||
33 | (define sieve-debug 0) | 34 | (define sieve-debug 0) |
34 | (define sieve-parser #t) | 35 | (define sieve-parser #t) |
35 | (define sieve-libdir "%LIBDIR%") | 36 | (define sieve-libdir "%LIBDIR%") |
36 | (define sieve-load-files '()) | 37 | (define sieve-load-files '()) |
38 | (define sieve-script-args '()) | ||
37 | 39 | ||
38 | (define error-count 0) | 40 | (define error-count 0) |
39 | (define current-token #f) | 41 | (define current-token #f) |
... | @@ -374,65 +376,81 @@ | ... | @@ -374,65 +376,81 @@ |
374 | 376 | ||
375 | ;;;; | 377 | ;;;; |
376 | 378 | ||
379 | ;;; Syntax tables. | ||
380 | ;;; A syntax table is a list of | ||
381 | ;;; Each entry is: (list NAME FUNCTION OPT-ARG-LIST REQ-ARG-LIST) | ||
382 | ;;; NAME is a string representing the input language keyword, | ||
383 | ;;; FUNCTION is a corresponding function: | ||
384 | ;;; (define (foo [arg [arg...]] . opt-args) | ||
385 | ;;; OPT-ARG-LIST is a list of optional arguments (tags), | ||
386 | ;;; REQ-ARG-LIST is a list of required (positional) arguments | ||
387 | |||
377 | (define (sieve-syntax-table-lookup table name) | 388 | (define (sieve-syntax-table-lookup table name) |
378 | (call-with-current-continuation | 389 | (let ((entry (assoc name table))) |
379 | (lambda (exit) | 390 | (if entry |
380 | (for-each (lambda (x) | 391 | (cdr entry) |
381 | (if (string=? (car x) name) | ||
382 | (exit (cdr x)))) | ||
383 | table) | ||
384 | #f))) | 392 | #f))) |
385 | 393 | ||
394 | (define-macro (sieve-syntax-table-add table name function req-arg-list opt-arg-list) | ||
395 | `(cond | ||
396 | ((not (list? ,opt-arg-list)) | ||
397 | (lex-error "sieve-syntax-table-add: opt-arg-list must be a list")) | ||
398 | ((not (list? ,req-arg-list)) | ||
399 | (lex-error "sieve-syntax-table-add: req-arg-list must be a list")) | ||
400 | ((not (or (eq? ,function #f) | ||
401 | (eq? ,function #t) | ||
402 | (procedure? ,function))) | ||
403 | (lex-error "sieve-syntax-table-add: bad type for function" ,function)) | ||
404 | (else | ||
405 | (set! ,table | ||
406 | (append ,table | ||
407 | (list | ||
408 | (list ,name ,function ,opt-arg-list ,req-arg-list))))))) | ||
409 | |||
410 | ;;;; | ||
411 | |||
412 | ;;;; Available syntax tables. | ||
413 | |||
386 | ;;;; Comparators | 414 | ;;;; Comparators |
387 | 415 | ||
388 | ;;; Each entry is (list COMP-NAME COMP-FUNCTION) | 416 | ;;; Syntax table for comparators. The opt-arg-list and req-arg-list have |
417 | ;;; no meaning for comparators, so they are ignored. The handler function | ||
418 | ;;; names must start with "comparator-" | ||
389 | (define sieve-comparator-table '()) | 419 | (define sieve-comparator-table '()) |
390 | 420 | ||
391 | (define (sieve-find-comparator name) | 421 | (define (sieve-find-comparator name) |
392 | (sieve-syntax-table-lookup sieve-comparator-table name)) | 422 | (sieve-syntax-table-lookup sieve-comparator-table name)) |
393 | 423 | ||
394 | (define (sieve-register-comparator name function) | 424 | (define (sieve-register-comparator name function) |
395 | (if (not (or (eq? function #f) | 425 | (sieve-syntax-table-add sieve-comparator-table name function '() '())) |
396 | (eq? function #t) | ||
397 | (procedure? function))) | ||
398 | (lex-error "sieve-register-comparator: bad type for function" | ||
399 | function)) | ||
400 | (set! sieve-comparator-table | ||
401 | (append sieve-comparator-table (list | ||
402 | (cons name function))))) | ||
403 | 426 | ||
427 | ;;;; Sieve Tests | ||
404 | 428 | ||
405 | ;;;; Command parsers | 429 | ;;; Syntax table for tests. Function names must start with "test-" |
406 | |||
407 | ;;; Each entry is: (list NAME FUNCTION OPT-ARG-LIST REQ-ARG-LIST) | ||
408 | ;;; OPT-ARG-LIST is a list of optional arguments (aka keywords, tags). | ||
409 | ;;; It consists of conses: (cons TAG-NAME FLAG) where FLAG is #t | ||
410 | ;;; if the tag requires an argument (e.g. :comparator <comp-name>), | ||
411 | ;;; and is #f otherwise. | ||
412 | ;;; REQ-ARG-LIST is a list of required (positional) arguments. It | ||
413 | ;;; is a list of argument types. | ||
414 | (define sieve-test-table '()) | 430 | (define sieve-test-table '()) |
415 | 431 | ||
416 | (define (sieve-find-test name) | 432 | (define (sieve-find-test name) |
417 | (sieve-syntax-table-lookup sieve-test-table name)) | 433 | (sieve-syntax-table-lookup sieve-test-table name)) |
418 | 434 | ||
419 | (define (sieve-register-test name function req-arg-list opt-arg-list) | 435 | (define (sieve-register-test name function req-arg-list opt-arg-list) |
420 | (DEBUG 100 "sieve-register-test" name req-arg-list opt-arg-list) | 436 | (sieve-syntax-table-add sieve-test-table name function |
421 | (cond | 437 | req-arg-list opt-arg-list)) |
422 | ((not (list? opt-arg-list)) | ||
423 | (lex-error "sieve-register-test: opt-arg-list must be a list")) | ||
424 | ((not (list? req-arg-list)) | ||
425 | (lex-error "sieve-register-test: req-arg-list must be a list")) | ||
426 | ((not (or (eq? function #f) | ||
427 | (eq? function #t) | ||
428 | (procedure? function))) | ||
429 | (lex-error "sieve-register-test: bad type for function" function)) | ||
430 | (else | ||
431 | (set! sieve-test-table | ||
432 | (append sieve-test-table | ||
433 | (list | ||
434 | (list name function opt-arg-list req-arg-list))))))) | ||
435 | 438 | ||
439 | ;;;; Sieve Actions | ||
440 | |||
441 | ;;; Syntax table for actions. Function names start with "action-" | ||
442 | (define sieve-action-table '()) | ||
443 | |||
444 | (define (sieve-find-action name) | ||
445 | (sieve-syntax-table-lookup sieve-action-table name)) | ||
446 | |||
447 | (define (sieve-register-action name function req-arg-list opt-arg-list) | ||
448 | (sieve-syntax-table-add sieve-action-table name function | ||
449 | req-arg-list opt-arg-list)) | ||
450 | |||
451 | ;;;; | ||
452 | |||
453 | ;;;; Command parsers | ||
436 | 454 | ||
437 | ;;; sieve-preprocess-arguments: Preprocess and group the arguments. Returns | 455 | ;;; sieve-preprocess-arguments: Preprocess and group the arguments. Returns |
438 | ;;; a cons whose car is a list of all optional arguments, and the cdr is | 456 | ;;; a cons whose car is a list of all optional arguments, and the cdr is |
... | @@ -555,6 +573,10 @@ | ... | @@ -555,6 +573,10 @@ |
555 | (cdr tag))))) | 573 | (cdr tag))))) |
556 | (car arg-list)))) | 574 | (car arg-list)))) |
557 | 575 | ||
576 | ;;;; | ||
577 | |||
578 | ;;;; Parser functions for tests | ||
579 | |||
558 | ;;; test-list = "(" test *("," test) ")" | 580 | ;;; test-list = "(" test *("," test) ")" |
559 | (define (sieve-parse-test-list) | 581 | (define (sieve-parse-test-list) |
560 | (do ((token (sieve-parse-test) (sieve-parse-test))) | 582 | (do ((token (sieve-parse-test) (sieve-parse-test))) |
... | @@ -692,35 +714,9 @@ | ... | @@ -692,35 +714,9 @@ |
692 | (sieve-exp-finish) | 714 | (sieve-exp-finish) |
693 | (require-semicolon)) | 715 | (require-semicolon)) |
694 | 716 | ||
695 | ;;; Actions | 717 | ;;;; |
696 | |||
697 | ;;; Each entry is: (list NAME FUNCTION OPT-ARG-LIST REQ-ARG-LIST) | ||
698 | ;;; NAME is a string representing the action name, | ||
699 | ;;; FUNCTION is a corresponding function: | ||
700 | ;;; (define (action-foo [arg [arg...]] . opt-args) | ||
701 | ;;; notice, that its name must begin with "action-" | ||
702 | ;;; OPT-ARG-LIST is a list of optional arguments (tags), | ||
703 | ;;; REQ-ARG-LIST is a list of required (positional) arguments | ||
704 | (define sieve-action-table '()) | ||
705 | |||
706 | (define (sieve-find-action name) | ||
707 | (sieve-syntax-table-lookup sieve-action-table name)) | ||
708 | 718 | ||
709 | (define (sieve-register-action name function req-arg-list opt-arg-list) | 719 | ;;;; Parser functions for actions |
710 | (cond | ||
711 | ((not (list? opt-arg-list)) | ||
712 | (lex-error "sieve-register-action: opt-arg-list must be a list")) | ||
713 | ((not (list? req-arg-list)) | ||
714 | (lex-error "sieve-register-action: req-arg-list must be a list")) | ||
715 | ((not (or (eq? function #f) | ||
716 | (eq? function #t) | ||
717 | (procedure? function))) | ||
718 | (lex-error "sieve-register-action: bad type for function" function)) | ||
719 | (else | ||
720 | (set! sieve-action-table | ||
721 | (append sieve-action-table | ||
722 | (list | ||
723 | (list name function opt-arg-list req-arg-list))))))) | ||
724 | 720 | ||
725 | (define (sieve-parse-action) | 721 | (define (sieve-parse-action) |
726 | (let* ((name (cdr current-token)) | 722 | (let* ((name (cdr current-token)) |
... | @@ -738,7 +734,9 @@ | ... | @@ -738,7 +734,9 @@ |
738 | (else | 734 | (else |
739 | (syntax-error "Unknown identifier: " name))))) | 735 | (syntax-error "Unknown identifier: " name))))) |
740 | 736 | ||
741 | ;;;; Parser | 737 | ;;;; |
738 | |||
739 | ;;;; The parser | ||
742 | 740 | ||
743 | (define (sieve-parse-command) | 741 | (define (sieve-parse-command) |
744 | (DEBUG 10 "sieve-parse-command" current-token) | 742 | (DEBUG 10 "sieve-parse-command" current-token) |
... | @@ -829,6 +827,8 @@ | ... | @@ -829,6 +827,8 @@ |
829 | (call-with-input-file filename sieve-parse-from-port)) | 827 | (call-with-input-file filename sieve-parse-from-port)) |
830 | (lambda args args)))) | 828 | (lambda args args)))) |
831 | 829 | ||
830 | ;;;; | ||
831 | |||
832 | ;;;; Code generator | 832 | ;;;; Code generator |
833 | 833 | ||
834 | (define sieve-exp '()) ;; Expression currently being built | 834 | (define sieve-exp '()) ;; Expression currently being built |
... | @@ -876,15 +876,18 @@ | ... | @@ -876,15 +876,18 @@ |
876 | 876 | ||
877 | ;;; Save the program | 877 | ;;; Save the program |
878 | 878 | ||
879 | (define (sieve-save-program outfile) | 879 | (define (sieve-save-program outfile guimb-header) |
880 | (call-with-output-file | 880 | (call-with-output-file |
881 | outfile | 881 | outfile |
882 | (lambda (port) | 882 | (lambda (port) |
883 | (display "#! /home/gray/mailutils/guimb/guimb --source\n!#\n" port) | 883 | (display "#! " port) |
884 | (if guimb-header | ||
885 | (display "%BINDIR%/guimb -s\n" port) | ||
886 | (display "%GUILE_BINDIR%/guile -s\n" port)) | ||
884 | (display (string-append | 887 | (display (string-append |
885 | ";;;; A Guile mailbox parser made from " filename) port) | 888 | "# Guile mailbox parser made from " filename) port) |
886 | (newline port) | 889 | (newline port) |
887 | (display ";;;; by sieve.scm, GNU %PACKAGE% %VERSION%" port) | 890 | (display "# by sieve.scm, GNU %PACKAGE% %VERSION%\n!#" port) |
888 | (newline port) | 891 | (newline port) |
889 | 892 | ||
890 | (display "(define sieve-parser #f)" port) | 893 | (display "(define sieve-parser #f)" port) |
... | @@ -894,6 +897,10 @@ | ... | @@ -894,6 +897,10 @@ |
894 | (newline port) | 897 | (newline port) |
895 | 898 | ||
896 | (display (string-append | 899 | (display (string-append |
900 | "(define sieve-libdir \"" sieve-libdir "\")") port) | ||
901 | (newline port) | ||
902 | |||
903 | (display (string-append | ||
897 | "(load \"" sieve-libdir "/sieve-core.scm\")") port) | 904 | "(load \"" sieve-libdir "/sieve-core.scm\")") port) |
898 | (newline port) | 905 | (newline port) |
899 | (for-each | 906 | (for-each |
... | @@ -906,40 +913,28 @@ | ... | @@ -906,40 +913,28 @@ |
906 | (newline port) | 913 | (newline port) |
907 | (display "(sieve-main)" port)))) | 914 | (display "(sieve-main)" port)))) |
908 | 915 | ||
916 | ;;;; | ||
917 | |||
909 | ;;;; Main | 918 | ;;;; Main |
910 | 919 | ||
911 | (define filename #f) | 920 | (define filename #f) |
912 | (define output #f) | 921 | (define output #f) |
922 | (define guimb-header #f) | ||
913 | 923 | ||
914 | (define (sieve-usage) | 924 | (define (sieve-usage) |
915 | (display "usage: sieve.scm [OPTIONS]\n") | 925 | (display "usage: sieve.scm [OPTIONS][mailbox]\n") |
916 | (display "GNU sieve.scm -- compile a Sieve program into Scheme code\n") | 926 | (display "GNU sieve.scm -- compile a Sieve program into Scheme code\n") |
917 | (display "Options are:\n") | 927 | (display "Options are:\n") |
918 | (display " -f, --file FILENAME Set input file name\n") | 928 | (display " -f, --file FILENAME Set input file name\n") |
919 | (display " -o, --output FILENAME Set output file name\n") | 929 | (display " -o, --output FILENAME Set output file name\n") |
920 | (display " -L, --lib-dir DIRNAME Set sieve library directory name\n") | 930 | (display " -L, --lib-dir DIRNAME Set sieve library directory name\n") |
921 | (display " -d, --debug LEVEL Set debugging level\n") | 931 | (display " -g, --guimb Make output file executable for guimb\n") |
932 | (display " -d, --debug LEVEL Set debugging level\n\n") | ||
933 | (display "If -o option is not given, the compiled program is executed\n") | ||
934 | (display "immediately. It operates on the user system mailbox unless\n") | ||
935 | (display "mailbox is given in the command line.\n") | ||
922 | (exit 0)) | 936 | (exit 0)) |
923 | 937 | ||
924 | (define (sieve-expand-filename name) | ||
925 | (let ((index (string-index name #\%))) | ||
926 | (if (or (not index) (= index (string-length name))) | ||
927 | name | ||
928 | (let ((ch (string-ref name (1+ index)))) | ||
929 | (string-append | ||
930 | (make-shared-substring name 0 index) | ||
931 | (case ch | ||
932 | ((#\%) | ||
933 | "%") | ||
934 | ((#\u) | ||
935 | user-name) | ||
936 | ((#\h) | ||
937 | (passwd:dir (getpwnam user-name))) | ||
938 | (else | ||
939 | (make-shared-substring name index 2))) | ||
940 | (sieve-expand-filename | ||
941 | (make-shared-substring name (+ index 2)))))))) | ||
942 | |||
943 | ;;; Parse command line | 938 | ;;; Parse command line |
944 | 939 | ||
945 | (use-modules (ice-9 getopt-long)) | 940 | (use-modules (ice-9 getopt-long)) |
... | @@ -952,6 +947,7 @@ | ... | @@ -952,6 +947,7 @@ |
952 | (value #t)) | 947 | (value #t)) |
953 | (lib-dir (single-char #\L) | 948 | (lib-dir (single-char #\L) |
954 | (value #t)) | 949 | (value #t)) |
950 | (guimb (single-char #\g)) | ||
955 | (help (single-char #\h)))) | 951 | (help (single-char #\h)))) |
956 | 952 | ||
957 | (define program-name (car (command-line))) | 953 | (define program-name (car (command-line))) |
... | @@ -969,28 +965,21 @@ | ... | @@ -969,28 +965,21 @@ |
969 | (set! sieve-libdir (cdr x))) | 965 | (set! sieve-libdir (cdr x))) |
970 | ((output) | 966 | ((output) |
971 | (set! output (cdr x))) | 967 | (set! output (cdr x))) |
968 | ((guimb) | ||
969 | (set! guimb-header #t)) | ||
972 | ((help) | 970 | ((help) |
973 | (sieve-usage)))))) | 971 | (sieve-usage)) |
972 | ('() | ||
973 | (set! sieve-script-args (cdr x))))))) | ||
974 | (getopt-long (command-line) grammar)) | 974 | (getopt-long (command-line) grammar)) |
975 | 975 | ||
976 | (if (not filename) | 976 | (cond |
977 | (begin | 977 | ((not filename) |
978 | (display program-name) | 978 | (display program-name) |
979 | (display ": missing input filename") | 979 | (display ": missing input filename") |
980 | (newline) | 980 | (newline) |
981 | (sieve-usage))) | 981 | (sieve-usage)) |
982 | 982 | ((not (file-exists? filename)) | |
983 | (define guimb? (catch #t | ||
984 | (lambda () | ||
985 | (let ((package mu-package)) | ||
986 | package)) | ||
987 | (lambda args #f))) | ||
988 | |||
989 | (if (and guimb? (string? user-name)) | ||
990 | (set! filename (sieve-expand-filename filename))) | ||
991 | |||
992 | (if (not (file-exists? filename)) | ||
993 | (begin | ||
994 | (display (string-append | 983 | (display (string-append |
995 | program-name | 984 | program-name |
996 | ": Input file " filename " does not exist.")) | 985 | ": Input file " filename " does not exist.")) |
... | @@ -1019,23 +1008,20 @@ | ... | @@ -1019,23 +1008,20 @@ |
1019 | (newline) | 1008 | (newline) |
1020 | (exit 1)) | 1009 | (exit 1)) |
1021 | (output | 1010 | (output |
1022 | (sieve-save-program output)) | 1011 | (sieve-save-program output guimb-header)) |
1023 | ((not guimb?) | ||
1024 | (display program-name) | ||
1025 | (display ": Either use guimb to compile and execute the script") | ||
1026 | (newline) | ||
1027 | (display "or use --output option to save the Scheme program.") | ||
1028 | (newline) | ||
1029 | (exit 1)) | ||
1030 | (else | 1012 | (else |
1031 | (let ((temp-file (tmpnam)) | 1013 | (let ((temp-file (tmpnam)) |
1032 | (saved-umask (umask #o077))) | 1014 | (saved-umask (umask #o077))) |
1033 | (sieve-save-program temp-file) | 1015 | (sieve-save-program temp-file) |
1034 | (load temp-file) | 1016 | (catch #t |
1017 | (lambda () | ||
1018 | (load temp-file)) | ||
1019 | (lambda (key . args) | ||
1020 | (apply display-error the-last-stack (current-error-port) args))) | ||
1035 | (delete-file temp-file) | 1021 | (delete-file temp-file) |
1036 | (umask saved-umask)))) | 1022 | (umask saved-umask)))) |
1037 | 1023 | ||
1038 | 1024 | ;;;; End of sieve.scm | |
1039 | 1025 | ||
1040 | 1026 | ||
1041 | 1027 | ... | ... |
-
Please register or sign in to post a comment