diff options
Diffstat (limited to 'fff.read.ss')
-rw-r--r-- | fff.read.ss | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/fff.read.ss b/fff.read.ss new file mode 100644 index 0000000..fa1aec2 --- /dev/null +++ b/fff.read.ss | |||
@@ -0,0 +1,166 @@ | |||
1 | ;;; (fff read) --- read an fff file into a scheme structure | ||
2 | ;; (C) Case Duckworth <acdw@acdw.net> | ||
3 | |||
4 | (import (scheme base) | ||
5 | (scheme case-lambda) | ||
6 | |||
7 | (fff parse) | ||
8 | |||
9 | (srfi 1) | ||
10 | (utf8)) | ||
11 | |||
12 | ;;; Parameters | ||
13 | |||
14 | (define chunk-size | ||
15 | (make-parameter 2048)) | ||
16 | |||
17 | (define ignore-comments? | ||
18 | (make-parameter #t)) | ||
19 | |||
20 | (define base-environment | ||
21 | (make-parameter '(("true" . #t) | ||
22 | ("false" . #f) | ||
23 | ("null" . null)))) | ||
24 | |||
25 | ;;; Transforming parsed intermediate representation into fff | ||
26 | |||
27 | (define (comment? it) | ||
28 | (eq? 'comment (car it))) | ||
29 | |||
30 | (define (singleton it) | ||
31 | (if (pair? it) | ||
32 | (if (not (pair? (cdr it))) | ||
33 | #t | ||
34 | (and (eq? 'ref (cadr it)) | ||
35 | (not (pair? (cddr it))) | ||
36 | 'ref)) | ||
37 | #f)) | ||
38 | |||
39 | (define (resolve-ref obj env) | ||
40 | (let ((ref (cddr obj))) | ||
41 | (if (assoc ref env) | ||
42 | (cons (car obj) | ||
43 | (cdr (assoc ref env))) | ||
44 | (error "Reference doesn't exist: " ref env)))) | ||
45 | |||
46 | (define (resolve-refs obj env) | ||
47 | ;;; Resolve references in OBJ using ENV. | ||
48 | (let loop ((items (cdr obj)) | ||
49 | (new-obj (list (car obj))) | ||
50 | (resolved-refs '())) | ||
51 | (if (null? items) | ||
52 | (values (reverse new-obj) | ||
53 | resolved-refs) | ||
54 | |||
55 | (let ((this (car items))) | ||
56 | (if (and (pair? (cdr this)) | ||
57 | (eq? 'ref (cadr this))) | ||
58 | (loop (cdr items) | ||
59 | (cons (resolve-ref this env) new-obj) | ||
60 | (cons (cddr this) resolved-refs)) | ||
61 | (loop (cdr items) | ||
62 | (cons this new-obj) | ||
63 | resolved-refs)))))) | ||
64 | |||
65 | (define (maybe-vector obj) | ||
66 | (let loop ((items (cdr obj)) | ||
67 | (type #f)) | ||
68 | (cond | ||
69 | ((comment? obj) | ||
70 | (cons 'comment | ||
71 | (apply string-append | ||
72 | (map (lambda (s) | ||
73 | (string-append s "\n")) | ||
74 | (cdr obj))))) | ||
75 | ((and (null? items) | ||
76 | (eq? type 'object)) | ||
77 | obj) | ||
78 | ((and (null? items) | ||
79 | (eq? type 'vector)) | ||
80 | (cons (car obj) | ||
81 | (list->vector (map cdr (cdr obj))))) | ||
82 | ((not (pair? items)) | ||
83 | obj) | ||
84 | (else | ||
85 | (loop (cdr items) | ||
86 | (if (caar items) | ||
87 | (if (eq? type 'vector) | ||
88 | (error "Item type mismatch" 'object type) | ||
89 | 'object) | ||
90 | (if (eq? type 'object) | ||
91 | (error "Item type mismatch" 'vector type) | ||
92 | 'vector))))))) | ||
93 | |||
94 | (define (%fff->scm fff env acc dupes) | ||
95 | (if (null? fff) | ||
96 | (map maybe-vector | ||
97 | (reverse | ||
98 | (filter | ||
99 | (lambda (x) (not (member (car x) dupes))) | ||
100 | acc))) | ||
101 | (let ((this (car fff)) | ||
102 | (rest (cdr fff))) | ||
103 | (cond | ||
104 | ((comment? this) | ||
105 | (if (ignore-comments?) | ||
106 | (%fff->scm (cdr fff) env acc dupes) | ||
107 | (%fff->scm rest env (cons this acc) dupes))) | ||
108 | ((singleton this) => | ||
109 | (lambda (x) | ||
110 | (%fff->scm rest | ||
111 | (cons this env) | ||
112 | (cons (if (eq? 'ref x) | ||
113 | (resolve-ref this (append env fff)) | ||
114 | this) | ||
115 | acc) | ||
116 | (if (eq? 'ref x) | ||
117 | (cons (cddr this) dupes) | ||
118 | dupes)))) | ||
119 | (else | ||
120 | (let-values (((resolved references) | ||
121 | (resolve-refs this (append env fff)))) | ||
122 | (%fff->scm rest | ||
123 | (cons this env) | ||
124 | (cons resolved acc) | ||
125 | (append references dupes)))))))) | ||
126 | |||
127 | (define fff->scm | ||
128 | (case-lambda | ||
129 | ((fff) | ||
130 | (%fff->scm fff (base-environment) '() '())) | ||
131 | ((fff env) | ||
132 | (%fff->scm fff env '() '())))) | ||
133 | |||
134 | ;;; Reading fff | ||
135 | |||
136 | (define (read-port port) | ||
137 | (let loop ((next (read-string (chunk-size) port)) | ||
138 | (blank? #f) | ||
139 | (acc '())) | ||
140 | (cond | ||
141 | ((or (eof-object? next) | ||
142 | (and blank? (equal? next ""))) | ||
143 | (close-input-port port) | ||
144 | (apply string-append (reverse acc))) | ||
145 | ((equal? next "") | ||
146 | (loop (read-string (chunk-size) port) | ||
147 | #t | ||
148 | (cons next acc))) | ||
149 | (else | ||
150 | (loop (read-string (chunk-size) port) | ||
151 | blank? | ||
152 | (cons next acc)))))) | ||
153 | |||
154 | (define (%read-fff port) | ||
155 | (or (fff->scm (parse-fff (read-port port))) | ||
156 | (error "Invalid fff" port))) | ||
157 | |||
158 | (define read-fff | ||
159 | (case-lambda | ||
160 | (() | ||
161 | (%read-fff (current-input-port))) | ||
162 | ((port) | ||
163 | (%read-fff port)))) | ||
164 | |||
165 | (define (read-fff-string str) | ||
166 | (call-with-port (open-input-string str) %read-fff)) | ||