diff --git a/member-file-old.scm b/member-file-old.scm deleted file mode 100644 index a62d3db..0000000 --- a/member-file-old.scm +++ /dev/null @@ -1,368 +0,0 @@ -;; -;; member-file.scm -;; -;; Member file parsing. -;; -;; ISC License -;; -;; Copyright 2023 Brmlab, z.s. -;; Dominik Pantůček -;; -;; Permission to use, copy, modify, and/or distribute this software -;; for any purpose with or without fee is hereby granted, provided -;; that the above copyright notice and this permission notice appear -;; in all copies. -;; -;; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL -;; WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED -;; WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE -;; AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR -;; CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS -;; OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -;; NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN -;; CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -;; - -(declare (unit member-file)) - -(module - member-file - ( - member-file-error-symbol - load-member-file - member-file-tests! - ) - - (import scheme - (chicken base) - (chicken string) - (chicken io) - (chicken irregex) - (chicken format) - (chicken random) - dictionary - month - period - testing - listing - ansi - configuration - progress) - - ;; Specification of known keys for various types of parsing - (define known-keys '(nick mail phone name born joined destroyed - ;; mail2 ID email suspended tel ;; Unknown keys - )) - (define start/stop-keys '(student suspend)) - (define multi-keys '(card desfire credit)) - (define validated-keys '(nick)) - - ;; Symbol used for error reporting in member file keys. - (define member-file-error-symbol - (string->symbol - (sprintf "error-symbol-~A" - (substring - (number->string (pseudo-random-real)) - 2)))) - - ;; Derived keys - (define start/stop-keys-keys - (map - string->symbol - (flatten - (map - (lambda (key) - (let ((skey (symbol->string key))) - (list (string-intersperse (list skey "start") "") - (string-intersperse (list skey "stop") "")))) - start/stop-keys)))) - - ;; All valid keys in one place for early validation - (define all-valid-keys - (append known-keys start/stop-keys-keys multi-keys)) - - ;; Member File Parser: remove comments from line and return the result - (define (line-remove-comments l) - (let ((si (substring-index "#" l))) - (if si - (if (= si 0) - "" - (car (string-chop l si))) - l))) - - ;; Reports error with highlighted source line(s), configured via the - ;; *member-file-check-syntax* parameter. - (define (report-line-error file-name lines highlight message) - (when (not (eq? (*member-file-check-syntax*) 'quiet)) - (progress-break - (print "Error in " file-name ": " message) - (newline) - (print-source-listing lines (list highlight) - (*member-file-context*) - a:error a:default - "" "" "...") - (newline))) - (if (eq? (*member-file-check-syntax*) 'error) - (exit 1) - (list member-file-error-symbol - (list highlight - message)))) - - ;; Parses given key-value line. Key is up to first space, value is the - ;; rest of the line. If the line doesn't contain anything, returns #f. - (define (split-member-line l . rest) - (let* ((file-name (if (not (null? rest)) (car rest) #f)) - (rest1 (if (not (null? rest)) (cdr rest) '())) - (lines (if (not (null? rest1)) (car rest1) #f)) - (rest2 (if (not (null? rest1)) (cdr rest1) '())) - (line-number (if (not (null? rest2)) (car rest2) #f)) - (lc0 (irregex-replace "^[ \\t]*" (line-remove-comments l) "")) - (lc (irregex-replace "[ \\t]*$" lc0 ""))) - (if (= (string-length lc) 0) - #f - (let ((dm (irregex-search (string->irregex "[ \\t]") lc))) - (if dm - (let* ((kei (irregex-match-start-index dm)) - (skey (substring lc 0 kei)) - (key (string->symbol skey)) - (v0 (substring lc kei)) - (val (irregex-replace "^[ \\t]*" v0 ""))) - (if (member key all-valid-keys) - (list key val) - (if file-name - (report-line-error file-name lines line-number - (sprintf "Invalid key ~A" key)) - (error 'split-member-line (sprintf "Invalid key ~A" key))))) - (if (member (string->symbol lc) all-valid-keys) - (if file-name - (report-line-error file-name lines line-number - (sprintf "No value for key ~A" lc)) - (error 'split-member-line (sprintf "No value for key ~A" lc))) - (if file-name - (report-line-error file-name lines line-number - (sprintf "No value for invalid key ~A" lc)) - (error 'split-member-line - (sprintf "No value for invalid key ~A" lc))))))))) - - ;; If given symbol represents start/stop symbol of either kind, - ;; returns a list of the symbol representing the type and start/stop - ;; symbol. It returns false otherwise. - (define (split-start/stop-symbol s) - (let* ((ss (symbol->string s)) - (mstart (irregex-search (string->irregex "(start|stop)") ss))) - (if mstart - (let* ((istart (irregex-match-start-index mstart)) - (bstring (substring ss 0 istart)) - (sstring (substring ss istart))) - (list (string->symbol bstring) - (string->symbol sstring))) - #f))) - - ;; Processes member line adding given value v to the dictionary d - ;; under key k. Special handling for start/stop symbols means given - ;; value is prepended to given start/stop key (student/suspend) as - ;; parsed month for later processing of student/suspend periods. - (define (process-member-line d k v line-number) - (let ((ss (split-start/stop-symbol k))) - (if ss - (let ((pk (car ss)) - (pd (cadr ss)) - (vl (string-split v " "))) - (if (null? vl) ; Should not happen - k/v parser already catches this - (error 'process-member-line "Missing date for start/stop symbol" k) - (let ((ds (car vl))) - (dict-set d pk - (cons (list pd (string->month ds) line-number) - (dict-ref d pk '())))))) - (case k - ((card desfire credit) (dict-set d k (cons v (dict-ref d k '())))) - (else - (if (eq? k member-file-error-symbol) - (dict-set d k (cons v (dict-ref d k '()))) - (dict-set d k (if (member k validated-keys) - (cons v line-number) - v)))))))) - - ;; Converts given key in member info dictionary from period markers - ;; list to periods. - (define (convert-member-key:markers->periods m k) - (if (dict-has-key? m k) - (let* ((res (period-markers->periods - (dict-ref m k '()))) - (ok? (car res)) - (periods (cadr res)) - (msg (caddr res)) - (line-number (cadddr res)) - (m1 (dict-set m k periods))) - (if ok? - m1 - (dict-set m1 member-file-error-symbol - (cons (list line-number msg) - (dict-ref m1 member-file-error-symbol '()))))) - m)) - - ;; Converts all given keys using period-markers->periods. - (define (convert-member-keys:markers->periods m . ks) - (let loop ((m m) - (ks ks)) - (if (null? ks) - m - (loop (convert-member-key:markers->periods m (car ks)) - (cdr ks))))) - - ;; Credit values contain amount as the first token. Anything after - ;; the first whitespace character is added as label of the credit - ;; transaction. The result is cons of number and string. - (define (convert-member-value:credit v) - (let* ((va (string-split v)) - (v0 (car va)) - (vr (substring v (string-length v0)))) - (cons (string->number v0) - vr))) - - ;; If member information dictionary contains 'credit, all elements of - ;; the list under this key are converted to cons of number (amount) - ;; and string (label / description). - (define (convert-member-key:credit m) - (if (dict-has-key? m 'credit) - (dict-set m 'credit - (reverse - (map convert-member-value:credit - (dict-ref m 'credit)))) - m)) - - ;; Converts the card id and any following string to card and label / - ;; description string cons. - (define (convert-member-value:card v) - (let* ((va (string-split v)) - (v0 (car va)) - (vr (irregex-replace - (irregex "^ *" 'u) - (substring v (string-length v0)) - ""))) - (cons v0 vr))) - - ;; Converts card ids to cons of two strings. - (define (convert-member-keys:card m . ks) - (let loop ((m m) - (ks ks)) - (if (null? ks) - m - (let ((k (car ks))) - (loop (if (dict-has-key? m k) - (dict-set m k (map convert-member-value:card (dict-ref m k))) - m) - (cdr ks)))))) - - ;; Converts given key to month if it is a string. Leaves untouched - ;; otherwise. - (define (convert-member-key:month m k) - (let ((v (dict-ref m k))) - (if (string? v) - (dict-set m k (string->month v)) - m))) - - ;; Adds file-name and lines information to the error key. - (define (convert-member-key:error m esym lines file-name) - (dict-set m esym - (cons file-name - (cons lines - (dict-ref m esym '()))))) - - ;; Check actual key error - (define (check-member-key-error key v) - (case key - ((nick) - (if (irregex-search (irregex " " 'u) v) - "Space in nick!" - #f)) - (else - #f))) - - ;; Adds error to the highlights - (define (add-member-error mr ln msg) - (dict-set mr member-file-error-symbol - (cons (list ln msg) - (dict-ref mr member-file-error-symbol '())))) - - ;; Performs actual validation, optionally adding error to the list - (define (validate-member-key m key v line-number) - (let ((res (check-member-key-error key v))) - (if res - (add-member-error m line-number res) - m))) - - ;; Gets cons of nick and line number and if validation fails, adds - ;; error to the error key. - (define (convert-member-key:validation m) - (let loop ((keys validated-keys) - (m m)) - (if (null? keys) - m - (let ((key (car keys))) - (loop (cdr keys) - (if (dict-has-key? m key) - (let ((v (dict-ref m key))) - (validate-member-key (dict-set m key (car v)) - key (car v) (cdr v))) - m)))))) - - ;; All conversions in one place, including error reporting. - (define (convert-member-keys m lines file-name) - (convert-member-key:error - (convert-member-key:validation - (convert-member-key:month - (convert-member-keys:card - (convert-member-key:credit - (convert-member-keys:markers->periods m 'suspend 'student)) - 'card 'desfire) - 'joined)) - member-file-error-symbol lines file-name)) - - ;; Fills-in the defaults - (define (make-default-member-info) - (dict-set - (make-dict) - 'joined - (*member-default-joined*))) - - ;; Processes all lines and returns a dictionary representing given - ;; member. - (define (parse-member-lines lines file-name) - (let loop ((ls lines) - (r (make-default-member-info)) - (line-number 1)) - (if (null? ls) - (convert-member-keys r lines file-name) - (let ((p (split-member-line (car ls) file-name lines line-number))) - (loop (cdr ls) - (if p - (process-member-line r (car p) (cadr p) line-number) - r) - (+ line-number 1)))))) - - ;; Loads lines from given file and parses them. - (define (load-member-file ffn) - (let* ((f (open-input-file ffn)) - (ls (read-lines f))) - (parse-member-lines ls ffn))) - - ;; Performs self-tests of the member-file module. - (define (member-file-tests!) - (run-tests - member-file - (test-equal? line-remove-comments (line-remove-comments "# all comment") "") - (test-equal? line-remove-comments (line-remove-comments "") "") - (test-equal? line-remove-comments (line-remove-comments "test # comment") "test ") - (test-equal? line-remove-comments (line-remove-comments "test") "test") - (test-equal? split-member-line (split-member-line "nick value") '(nick "value")) - (test-equal? split-member-line (split-member-line " nick value") '(nick "value")) - (test-equal? split-member-line (split-member-line " nick value ") '(nick "value")) - (test-equal? split-member-line (split-member-line " nick value1 value2 ") '(nick "value1 value2")) - (test-exn split-member-line (split-member-line "key value")) - (test-equal? split-start/stop-symbol (split-start/stop-symbol 'suspendstart) '(suspend start)) - (test-equal? split-start/stop-symbol (split-start/stop-symbol 'teststop) '(test stop)) - (test-false split-start/stop-symbol (split-start/stop-symbol 'normalkey)) - )) - - )