#!/usr/bin/guile \ -e main -s !# (add-to-load-path (dirname (current-filename))) (use-modules (srfi srfi-1) (srfi srfi-8) (srfi srfi-9) (srfi srfi-26) (ice-9 rdelim) (ice-9 peg) (patterns)) ;; var is the field to test ;; symb is the expected value (define-syntax-rule (ensure-symbol var symb) (unless (eqv? var symb) (throw 'symbol-not-equal "Expected ~s, got ~s~%" symb var))) (define (read-csv port) "Read from port into a peg:tree." (let* ((rawstr (read-delimited "" port)) (match (match-pattern file rawstr)) (tree (keyword-flatten '(line) (peg:tree match)))) tree)) ;;; TODO sometimes part is a symbol instead of a list, ;;; somehow an earlier part got consed on? or something? (define (get-part-data part) "Get data from field-part cells" (if (symbol? part) #f (begin (ensure-symbol (car part) 'field-part) (cadr part)))) (define (flatten-1 lst) "If list is of length one return the first element, otherwise return the full list" (if (= 1 (length lst)) (car lst) lst)) ;; ('line ('field ('field-part data) ...) ...) ;; -> ({(data ...) | data} ...) (define (clean-peg-tree tree) "Clean up input from read-csv, returns a more bare bones tree" (map (lambda (line) ; for every line (ensure-symbol (car line) 'line) (map (lambda (field) ; for every field in line (and (not (symbol? field)) ; if a symbol no data is present ;; else extract the data (begin (ensure-symbol (car field) 'field) (flatten-1 (map get-part-data (cdr field)))))) (cdr line))) tree)) ;;; Takes (car (clean-peg-tree csv)) ;;; name is a string ;;; returns a record-type, a constructor, a predicate procedure, ;;; and an association list of getters (define (create-csv-record name field-strings) "Creates datatypes from csv records" (let ((full-name (string-append "csv:" name)) (fields (map string->symbol field-strings))) (let* ((record-type (make-record-type full-name fields)) (make-record (record-constructor record-type fields)) (predicate (record-predicate record-type)) (accessors (map cons fields (map (cut record-accessor record-type <>) fields)))) (values record-type make-record predicate accessors)))) ;;; filename -> [book] (define (load-data csv-file-name) (let* ((csv (call-with-input-file csv-file-name read-csv)) (tree (clean-peg-tree csv)) (fields (car tree)) (data (cdr tree))) (receive (book make-book book? book-getters) (create-csv-record "book" fields) (map (cut apply make-book <>) data))))