Commit | Line | Data |
---|---|---|
f14c9685 AW |
1 | ;;; figl |
2 | ;;; Copyright (C) 2013 Andy Wingo <wingo@pobox.com> | |
3 | ;;; | |
4 | ;;; Figl is free software: you can redistribute it and/or modify it | |
5 | ;;; under the terms of the GNU Lesser General Public License as | |
6 | ;;; published by the Free Software Foundation, either version 3 of the | |
7 | ;;; License, or (at your option) any later version. | |
8 | ;;; | |
9 | ;;; Figl is distributed in the hope that it will be useful, but WITHOUT | |
10 | ;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
11 | ;;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General | |
12 | ;;; Public License for more details. | |
13 | ;;; | |
14 | ;;; You should have received a copy of the GNU Lesser General Public | |
15 | ;;; License along with this program. If not, see | |
16 | ;;; <http://www.gnu.org/licenses/>. | |
17 | ||
18 | ;;; Commentary: | |
19 | ;; | |
20 | ;; figl is the Foreign Interface to GL. | |
21 | ;; | |
22 | ;;; Code: | |
23 | ||
24 | (define-module (figl parse) | |
25 | #:use-module (figl config) | |
26 | #:use-module (sxml simple) | |
f9ad5a88 AW |
27 | #:use-module ((sxml xpath) #:hide (filter)) |
28 | #:use-module (sxml transform) | |
8925f36f | 29 | #:use-module (sxml fold) |
bb894c9d | 30 | #:use-module ((srfi srfi-1) #:select (filter fold append-map filter-map)) |
8925f36f | 31 | #:use-module (srfi srfi-9) |
f9ad5a88 | 32 | #:use-module (texinfo docbook) |
f14c9685 | 33 | #:use-module (ice-9 ftw) |
092cacd7 | 34 | #:use-module (ice-9 rdelim) |
227eae66 | 35 | #:use-module (ice-9 match) |
8925f36f AW |
36 | #:export (gl-definition? |
37 | make-gl-definition | |
38 | gl-definition-name | |
bb894c9d | 39 | gl-definition-prototypes |
8925f36f AW |
40 | gl-definition-documentation |
41 | gl-definition-copyright | |
092cacd7 AW |
42 | parse-gl-definitions |
43 | ||
44 | gl-enumeration? | |
45 | make-gl-enumeration | |
46 | gl-enumeration-category | |
47 | gl-enumeration-values | |
48 | parse-gl-enumerations)) | |
8925f36f AW |
49 | |
50 | (define-record-type gl-definition | |
bb894c9d | 51 | (make-gl-definition name prototypes documentation copyright) |
8925f36f AW |
52 | gl-definition? |
53 | (name gl-definition-name) | |
bb894c9d | 54 | (prototypes gl-definition-prototypes) |
8925f36f AW |
55 | (documentation gl-definition-documentation) |
56 | (copyright gl-definition-copyright)) | |
f14c9685 | 57 | |
092cacd7 AW |
58 | ;; values := (name . number) ... |
59 | (define-record-type gl-enumeration | |
60 | (make-gl-enumeration category values) | |
61 | gl-enumeration? | |
62 | (category gl-enumeration-category) | |
63 | (values gl-enumeration-values)) | |
64 | ||
f14c9685 AW |
65 | (define *namespaces* |
66 | '((mml . "http://www.w3.org/1998/Math/MathML"))) | |
67 | ||
68 | (define *entities* | |
f9ad5a88 AW |
69 | '(;; From http://www.w3.org/TR/MathML2/mmlextra.html |
70 | (af . "\u2061") ;; Function application. | |
71 | (it . "\u2062") ;; Invisible times. | |
72 | ;; http://www.w3.org/TR/MathML2/isonum.html | |
73 | (plus . "\u002B") ;; Plus sign. | |
74 | (times . "\u00D7") ;; Multiplication sign. | |
75 | ;; http://www.w3.org/TR/MathML2/isotech.html | |
76 | (Prime . "\u2033") ;; Double prime. | |
77 | (le . "\u2264") ;; Less than or equal to. | |
78 | (ne . "\u2260") ;; Not equal to. | |
79 | (minus . "\u2212") ;; Minus sign. | |
80 | ;; http://www.w3.org/TR/MathML2/isoamsc.html | |
81 | (lceil . "\u2308") ;; Left ceiling. | |
82 | (rceil . "\u2309") ;; Right ceiling. | |
83 | (lfloor . "\u230A") ;; Left floor. | |
84 | (rfloor . "\u230B") ;; Right floor. | |
85 | ;; http://www.w3.org/TR/MathML2/mmlalias.html | |
86 | (DoubleVerticalBar . "\u2225") ;; Parallel to. | |
87 | (LeftFloor . "\u230A") ;; Left floor. | |
88 | (RightFloor . "\u230B") ;; Right floor. | |
89 | (LeftCeiling . "\u2308") ;; Left ceiling. | |
90 | (RightCeiling . "\u2309") ;; Right ceiling. | |
91 | (CenterDot . "\u00B7") ;; Middle dot. | |
92 | (VerticalBar . "\u2223") ;; Divides. | |
93 | (PartialD . "\u2202") ;; Partial derivative. | |
94 | ;; http://www.w3.org/TR/MathML2/mmlextra.html | |
95 | (Hat . "\u005E") ;; Circumflex accent. | |
96 | ;; http://www.w3.org/TR/MathML2/isogrk3.html | |
97 | (Delta . "\u0394") ;; Greek capital letter delta. | |
98 | (Sigma . "\u03A3") ;; Greek capital letter sigma. | |
99 | ;; Misc. | |
100 | (nbsp . "\u00A0") | |
101 | )) | |
f14c9685 AW |
102 | |
103 | (define (default-entity-handler port name) | |
104 | (format (current-warning-port) | |
105 | "~a:~a:~a: undefined entitity: &~a;\n" | |
106 | (or (port-filename port) "<unknown file>") | |
107 | (port-line port) (port-column port) | |
108 | name) | |
109 | (symbol->string name)) | |
110 | ||
f9ad5a88 AW |
111 | (define dbmathml |
112 | "http://www.oasis-open.org/docbook/xml/mathml/1.1CR1/dbmathml.dtd") | |
113 | ||
114 | (define (docbook-with-mathml-handler docname systemid internal) | |
115 | (unless (equal? systemid dbmathml) | |
116 | (warn "unexpected doctype" docname systemid internal)) | |
117 | (values #:entities *entities* #:namespaces *namespaces*)) | |
118 | ||
119 | (define (trim-whitespace-left str) | |
120 | (let ((first (and (not (string-null? str)) | |
121 | (string-ref str 0)))) | |
122 | (if (and first (char-whitespace? first)) | |
123 | (string-append (string first) (string-trim str char-whitespace?)) | |
124 | str))) | |
125 | ||
126 | (define (trim-whitespace-right str) | |
127 | (let ((last (and (not (string-null? str)) | |
128 | (string-ref str (1- (string-length str)))))) | |
129 | (if (and last (char-whitespace? last)) | |
130 | (string-append (string-trim-right str char-whitespace?) (string last)) | |
131 | str))) | |
132 | ||
133 | (define (trim-whitespace str) | |
134 | (trim-whitespace-left | |
135 | (trim-whitespace-right str))) | |
136 | ||
137 | (define (zap-whitespace sxml) | |
138 | (define (not-whitespace x) | |
139 | (or (not (string? x)) | |
140 | (not (string-every char-whitespace? x)))) | |
141 | (pre-post-order sxml | |
142 | `((*default* . ,(lambda (tag . body) | |
143 | (cons tag | |
144 | (filter not-whitespace body)))) | |
145 | (*text* . ,(lambda (tag text) | |
146 | (if (string? text) | |
147 | (trim-whitespace text) | |
148 | text)))))) | |
149 | ||
53215235 AW |
150 | (define (parse-man-xml version filename) |
151 | (define subdir (format #f "man~A" version)) | |
a6a00658 | 152 | (call-with-input-file (in-vicinity (upstream-doc) |
53215235 | 153 | (in-vicinity subdir filename)) |
f14c9685 | 154 | (lambda (port) |
f9ad5a88 AW |
155 | (zap-whitespace |
156 | (xml->sxml port #:declare-namespaces? #t | |
157 | #:default-entity-handler default-entity-handler | |
158 | #:doctype-handler docbook-with-mathml-handler))))) | |
f14c9685 | 159 | |
53215235 AW |
160 | (define (xml-files version) |
161 | (define subdir (format #f "man~A" version)) | |
a6a00658 | 162 | (scandir (in-vicinity (upstream-doc) subdir) |
f14c9685 AW |
163 | (lambda (x) (string-suffix? ".xml" x)))) |
164 | ||
f9ad5a88 AW |
165 | (define (take-first proc) |
166 | (lambda (xml) | |
167 | (let ((res (proc xml))) | |
168 | (and (pair? res) (car res))))) | |
169 | ||
170 | (define xml-name | |
171 | (take-first (sxpath '(refentry refnamediv refname *text*)))) | |
172 | ||
173 | (define xml-purpose | |
174 | (take-first (sxpath '(refentry refnamediv refpurpose *text*)))) | |
175 | ||
bb894c9d AW |
176 | (define xml-funcprototypes |
177 | (sxpath '(refentry refsynopsisdiv funcsynopsis funcprototype))) | |
f9ad5a88 AW |
178 | |
179 | (define xml-parameters | |
180 | (take-first (sxpath '(refentry (refsect1 (@ id (equal? "parameters"))))))) | |
181 | ||
182 | (define xml-description | |
183 | (take-first (sxpath '(refentry (refsect1 (@ id (equal? "description"))))))) | |
184 | ||
185 | (define xml-errors | |
186 | (take-first (sxpath '(refentry (refsect1 (@ id (equal? "errors"))))))) | |
187 | ||
8925f36f AW |
188 | (define xml-copyright |
189 | (take-first (sxpath '(refentry (refsect1 (@ id (equal? "Copyright"))))))) | |
190 | ||
0e9b22c2 | 191 | (define (string->gl-type str) |
09522815 AW |
192 | (string->symbol |
193 | (string-join (string-split (string-trim-both str) #\space) "-"))) | |
bb894c9d AW |
194 | |
195 | (define (parse-prototypes sxml) | |
196 | (define all-names | |
197 | (match sxml | |
198 | ((('funcprototype ('funcdef return-type ('function names)) | |
199 | . _) | |
200 | ...) | |
201 | names))) | |
202 | ||
b002944d AW |
203 | (define (redundant-variant? s shun-suffix prefer-suffix) |
204 | (and (string-suffix? shun-suffix s) | |
205 | (member (string-append (substring s 0 (- (string-length s) | |
206 | (string-length shun-suffix))) | |
207 | prefer-suffix) | |
208 | all-names))) | |
209 | ||
bb894c9d AW |
210 | (define (skip? s) |
211 | (or | |
ca09631c AW |
212 | ;; Skip double variants if we have a float variant. |
213 | ;; (http://www.opengl.org/wiki/Common_Mistakes#GL_DOUBLE). | |
b002944d AW |
214 | (redundant-variant? s "d" "f") |
215 | ||
bb894c9d | 216 | ;; Skip byte variants if there is a short variant. |
b002944d AW |
217 | (redundant-variant? s "b" "s") |
218 | ||
bb894c9d | 219 | ;; Skip short variants if there is an int variant. |
b002944d AW |
220 | (redundant-variant? s "s" "i") |
221 | ||
222 | ;; Skip packed setters like glVertex3fv if e.g. glVertex3f exists. | |
223 | (redundant-variant? s "v" "") | |
224 | (redundant-variant? s "dv" "fv") | |
225 | (redundant-variant? s "bv" "sv") | |
226 | (redundant-variant? s "sv" "iv"))) | |
bb894c9d AW |
227 | |
228 | (filter-map | |
229 | (lambda (sxml) | |
230 | (match sxml | |
231 | (('funcprototype ('funcdef return-type ('function (? skip?))) | |
232 | . _) | |
233 | #f) | |
234 | (('funcprototype ('funcdef return-type ('function name)) | |
235 | ('paramdef ('parameter "void"))) | |
236 | `(,(string->symbol name) | |
237 | -> ,(string->gl-type return-type))) | |
238 | (('funcprototype ('funcdef return-type ('function name)) | |
239 | ('paramdef ptype ('parameter pname)) | |
240 | ...) | |
241 | `(,(string->symbol name) | |
242 | ,@(map (lambda (pname ptype) | |
243 | (list (string->symbol pname) | |
244 | (string->gl-type ptype))) | |
245 | pname ptype) | |
246 | -> ,(string->gl-type return-type))))) | |
247 | sxml)) | |
f9ad5a88 | 248 | |
227eae66 AW |
249 | (define (collapse-fragments nodeset) |
250 | (match nodeset | |
251 | ((('*fragment* elts ...) nodes ...) | |
252 | (append (collapse-fragments elts) | |
253 | (collapse-fragments nodes))) | |
254 | ((((and tag (? symbol?)) elts ...) nodes ...) | |
255 | (acons tag (collapse-fragments elts) (collapse-fragments nodes))) | |
256 | ((elt nodes ...) | |
257 | (cons elt (collapse-fragments nodes))) | |
258 | (() '()))) | |
259 | ||
260 | (define (list-intersperse src-l elem) | |
261 | (if (null? src-l) src-l | |
262 | (let loop ((l (cdr src-l)) (dest (cons (car src-l) '()))) | |
263 | (if (null? l) (reverse dest) | |
264 | (loop (cdr l) (cons (car l) (cons elem dest))))))) | |
265 | ||
8925f36f AW |
266 | (define (lift-tables sdocbook) |
267 | ;; Like sdocbook-flatten, but tweaked to lift tables from inside | |
268 | ;; paras, but not paras from inside tables. Pretty hacky stuff. | |
269 | (define *sdocbook-block-commands* | |
270 | '(informaltable programlisting variablelist)) | |
271 | ||
272 | (define (inline-command? command) | |
273 | (not (memq command *sdocbook-block-commands*))) | |
274 | ||
275 | (define (fhere str accum block cont) | |
276 | (values (cons str accum) | |
277 | block | |
278 | cont)) | |
279 | (define (fdown node accum block cont) | |
280 | (match node | |
281 | ((command (and attrs ('% . _)) body ...) | |
282 | (values body '() '() | |
283 | (lambda (accum block) | |
284 | (values | |
285 | `(,command ,attrs ,@(reverse accum)) | |
286 | block)))) | |
287 | ((command body ...) | |
288 | (values body '() '() | |
289 | (lambda (accum block) | |
290 | (values | |
291 | `(,command ,@(reverse accum)) | |
292 | block)))))) | |
293 | (define (fup node paccum pblock pcont kaccum kblock kcont) | |
294 | (call-with-values (lambda () (kcont kaccum kblock)) | |
295 | (lambda (ret block) | |
296 | (if (inline-command? (car ret)) | |
297 | (values (cons ret paccum) (append kblock pblock) pcont) | |
298 | (values paccum (append kblock (cons ret pblock)) pcont))))) | |
299 | (call-with-values | |
300 | (lambda () (foldts*-values fdown fup fhere sdocbook '() '() #f)) | |
301 | (lambda (accum block cont) | |
302 | (append (reverse accum) | |
303 | (reverse block) | |
304 | )))) | |
305 | ||
f9ad5a88 AW |
306 | (define *rules* |
307 | `((refsect1 | |
8925f36f | 308 | *preorder* |
f9ad5a88 | 309 | . ,(lambda (tag id . body) |
8925f36f AW |
310 | (append-map (lambda (nodeset) |
311 | (map | |
312 | (lambda (x) | |
313 | (pre-post-order x *rules*)) | |
314 | nodeset)) | |
3c9b6116 AW |
315 | (map lift-tables |
316 | (match body | |
317 | ((('title _) body ...) body) | |
318 | (_ body)))))) | |
f9ad5a88 AW |
319 | (variablelist |
320 | ((varlistentry | |
321 | . ,(lambda (tag term . body) | |
227eae66 | 322 | `(entry (% (heading ,@(cdar term))) ,@(apply append body)))) |
f9ad5a88 | 323 | (listitem |
227eae66 AW |
324 | . ,(lambda (tag . body) |
325 | (map (lambda (x) | |
326 | (if (string? x) | |
327 | `(para ,x) | |
328 | x)) | |
329 | body))) | |
330 | (term | |
f9ad5a88 | 331 | . ,(lambda (tag . rest) |
227eae66 | 332 | `((itemx ,@rest))))) |
f9ad5a88 | 333 | . ,(lambda (tag . body) |
227eae66 | 334 | `(table (% (formatter (asis))) ,@body))) |
8925f36f AW |
335 | (trademark |
336 | . ,(match-lambda* | |
337 | ((_ ('@ ('class "copyright"))) '(copyright)))) | |
f9ad5a88 AW |
338 | (parameter |
339 | . ,(lambda (tag body) | |
340 | `(var ,body))) | |
341 | (type | |
342 | . ,(lambda (tag body) | |
343 | `(code ,body))) | |
344 | (constant | |
345 | . ,(lambda (tag . body) | |
346 | `(code . ,body))) | |
227eae66 AW |
347 | (code |
348 | . ,(lambda (tag . body) | |
349 | `(code . ,body))) | |
f9ad5a88 AW |
350 | (function |
351 | . ,(lambda (tag body . ignored) | |
352 | (or (null? ignored) (warn "ignored function tail" ignored)) | |
353 | `(code ,body))) | |
354 | (emphasis | |
227eae66 AW |
355 | . ,(match-lambda* |
356 | ((_) "") | |
8925f36f AW |
357 | ((_ ('@ ('role "bold")) (and body (? string?))) |
358 | `(strong ,(string-trim-both body))) | |
227eae66 AW |
359 | ((_ ('@ ('role "bold")) . body) `(strong ,@body)) |
360 | ((_ body) `(var ,body)))) | |
361 | (citerefentry | |
362 | . ,(lambda (tag contents) | |
363 | contents)) | |
364 | (refentrytitle | |
365 | . ,(lambda (tag contents) | |
366 | `(code ,contents))) | |
367 | (inlineequation | |
368 | . ,(lambda (tag contents) | |
369 | contents)) | |
370 | (informalequation | |
371 | . ,(lambda (tag contents) | |
372 | contents)) | |
373 | (informaltable | |
374 | . ,(lambda (tag attrs tgroup) | |
375 | tgroup)) | |
376 | (tgroup | |
377 | ((thead | |
378 | . ,(lambda (tag . rows) | |
379 | rows)) | |
380 | (colspec | |
381 | . ,(lambda _ | |
382 | #f)) | |
383 | (tbody | |
384 | . ,(lambda (tag . rows) | |
385 | rows)) | |
386 | (row | |
387 | . ,(lambda (tag first . rest) | |
8925f36f AW |
388 | `(entry (% (heading ,@first)) |
389 | (para ,@(apply | |
390 | append | |
391 | (list-intersperse rest '(", "))))))) | |
227eae66 AW |
392 | (entry |
393 | . ,(match-lambda* | |
8925f36f AW |
394 | ((_) '()) |
395 | ((_ ('@ . _)) '()) | |
396 | ((_ ('@ . _) x ...) x) | |
397 | ((_ x ...) x)))) | |
227eae66 AW |
398 | . ,(lambda (tag attrs . contents) |
399 | `(table (% (formatter (asis))) | |
400 | ,@(apply append (filter identity contents))))) | |
401 | ||
402 | ;; Poor man's mathml. | |
403 | (mml:math | |
404 | . ,(lambda (tag . contents) | |
3c9b6116 | 405 | `(r . ,(collapse-fragments contents)))) |
227eae66 AW |
406 | (mml:mn |
407 | . ,(lambda (tag n . rest) | |
408 | (if (pair? rest) | |
409 | `(*fragment* ,n . ,rest) | |
410 | n))) | |
411 | (mml:mi | |
412 | . ,(case-lambda | |
413 | ((tag contents) | |
414 | `(code ,contents)) | |
415 | ((tag attrs contents) | |
416 | (match attrs | |
417 | (('@ (mathvariant "italic")) | |
418 | `(var ,contents)) | |
419 | (_ `(code ,contents)))))) | |
420 | ;; It would be possible to represent a matrix as a @multitable, but | |
421 | ;; Guile doesn't really have support for that. So instead print | |
422 | ;; each row in parentheses. | |
423 | (mml:mtable | |
424 | ((mml:mtr | |
425 | . ,(lambda (tag . body) | |
426 | `("(" ,@(list-intersperse body " ") ")"))) | |
427 | (mml:mtd | |
428 | . ,(match-lambda* | |
429 | ((tag ('@ . _) body ...) | |
430 | `(*fragment* ,@body)) | |
431 | ((tag body ...) | |
432 | `(*fragment* ,@body))))) | |
433 | . ,(lambda (tag . rows) | |
434 | ;; Rely on outer mfence for outer parens, if any | |
435 | (let ((rows (if (and (pair? rows) (eq? (caar rows) '@)) | |
436 | (cdr rows) | |
437 | rows))) | |
438 | `(*fragment* ,@(apply append (list-intersperse rows '(", "))))))) | |
439 | (mml:mspace | |
440 | . ,(lambda (tag . _) | |
441 | " ")) | |
442 | (mml:msup | |
443 | . ,(lambda (tag base exponent) | |
444 | `(*fragment* ,base "^" ,exponent))) | |
445 | (mml:msub | |
446 | . ,(lambda (tag base exponent) | |
447 | `(*fragment* ,base "_" ,exponent))) | |
448 | (mml:mover | |
449 | . ,(lambda (tag base over) | |
450 | `(*fragment* ,base ,over))) | |
451 | (mml:munderover | |
452 | . ,(lambda (tag under base over) | |
453 | `(*fragment* ,under ,base ,over))) | |
454 | (mml:mfrac | |
455 | . ,(lambda (tag num denom) | |
456 | `(*fragment* ,num "/" ,denom))) | |
457 | (mml:msqrt | |
458 | . ,(lambda (tag base) | |
459 | `(*fragment* "√" ,base))) | |
460 | (mml:infinity | |
461 | . ,(lambda (tag) | |
462 | "∞")) | |
463 | (mml:mo | |
464 | . ,(lambda (tag operator) | |
465 | operator)) | |
466 | (mml:mrow | |
467 | . ,(lambda (tag . contents) | |
468 | `(*fragment* . ,contents))) | |
469 | (mml:mfenced | |
470 | . ,(lambda (tag attrs left . right) | |
471 | `(*fragment* ,@(assq-ref attrs 'open) | |
472 | ,left | |
473 | "," | |
474 | ,@right | |
475 | ,@(assq-ref attrs 'close)))) | |
f9ad5a88 AW |
476 | (*text* |
477 | . ,(lambda (tag text) | |
478 | text)) | |
479 | ,@*sdocbook->stexi-rules*)) | |
480 | ||
481 | (define (sdocbook->stexi sdocbook) | |
482 | (pre-post-order sdocbook *rules*)) | |
483 | ||
484 | ;; Produces an stexinfo fragment. | |
485 | (define (generate-documentation purpose parameters description errors) | |
8925f36f | 486 | `(*fragment* |
3c9b6116 AW |
487 | (para ,(string-append (string (char-upcase (string-ref purpose 0))) |
488 | (substring purpose 1) | |
489 | ".")) | |
8925f36f AW |
490 | ,@(if parameters (sdocbook->stexi parameters) '()) |
491 | ,@(if description (sdocbook->stexi description) '()) | |
492 | ,@(if errors (sdocbook->stexi errors) '()))) | |
f9ad5a88 AW |
493 | |
494 | (define (xml->definition xml) | |
bb894c9d AW |
495 | (let ((prototypes (parse-prototypes (xml-funcprototypes xml)))) |
496 | (and (pair? prototypes) | |
497 | (make-gl-definition (xml-name xml) | |
498 | prototypes | |
499 | (generate-documentation (xml-purpose xml) | |
500 | (xml-parameters xml) | |
501 | (xml-description xml) | |
502 | (xml-errors xml)) | |
503 | (and=> (xml-copyright xml) | |
504 | (lambda (c) | |
505 | `(*fragment* ,@(sdocbook->stexi c)))))))) | |
8925f36f AW |
506 | |
507 | (define (parse-gl-definitions version) | |
bb894c9d AW |
508 | (filter-map (lambda (file) |
509 | (xml->definition (parse-man-xml version file))) | |
510 | (xml-files version))) | |
092cacd7 AW |
511 | |
512 | (define (trim-comment line) | |
513 | (cond | |
514 | ((string-index line #\#) | |
515 | => (lambda (idx) (substring line 0 idx))) | |
516 | (else line))) | |
517 | ||
518 | (define (expand-camel-case s) | |
519 | (define (add-humps humps out more?) | |
520 | (match humps | |
521 | (() out) | |
522 | ((head) | |
523 | (if (null? out) | |
524 | humps | |
525 | (cons* head #\- out))) | |
526 | ((head tail ...) | |
527 | (let ((out (if (null? out) | |
528 | tail | |
529 | (append tail (cons #\- out))))) | |
530 | (if more? | |
531 | (cons* head #\- out) | |
532 | (cons head out)))))) | |
533 | (let lp ((in (string->list s)) (humps '()) (out '())) | |
534 | (match in | |
535 | (() | |
536 | (list->string (reverse (add-humps humps out #f)))) | |
537 | ((c in ...) | |
538 | (if (and (char-lower-case? c) | |
539 | ;; Try to keep subtokens like 12x3 in one piece. | |
540 | (or (null? humps) | |
541 | (not (and-map char-numeric? humps)))) | |
542 | (lp in '() (cons c (add-humps humps out #t))) | |
543 | (lp in (cons (char-downcase c) humps) out)))))) | |
544 | ||
545 | (define (mangle-name name) | |
546 | (string->symbol | |
547 | (string-join (map expand-camel-case (string-split name #\_)) | |
548 | "-"))) | |
549 | ||
550 | (define (parse-number num) | |
551 | (cond | |
552 | ((equal? "0xFFFFFFFFu" num) | |
553 | #xFFFFFFFF) | |
554 | ((equal? "0xFFFFFFFFFFFFFFFFull" num) | |
555 | #xFFFFFFFFFFFFFFFF) | |
556 | ((string-prefix? "0x" num) | |
557 | (string->number (substring num 2) 16)) | |
558 | ((string-prefix? "GL_" num) | |
559 | (cons #f (mangle-name (substring num 3)))) | |
560 | (else | |
561 | (string->number num)))) | |
562 | ||
563 | (define (read-line-and-trim-comment port) | |
564 | (let ((line (read-line port))) | |
565 | (if (eof-object? line) | |
566 | line | |
567 | (string-trim-both (trim-comment line))))) | |
568 | ||
569 | (define (resolve-enumerations enums) | |
570 | ;; We shouldn't fail to resolve anything, but there are a couple bugs | |
571 | ;; in enum.spec currently: | |
572 | ;; http://www.khronos.org/bugzilla/show_bug.cgi?id=787. Until they | |
573 | ;; are fixed, allow resolution to fail. | |
574 | (define (resolve-value category name value) | |
575 | (match value | |
576 | (#f #f) | |
577 | ((? number?) | |
578 | value) | |
579 | ((#f . (and name (? symbol?))) | |
580 | (resolve-value category name category)) | |
581 | ((? symbol?) | |
582 | (resolve-value value name (assq-ref (assq-ref enums value) name))))) | |
583 | (let lp ((in enums) (out '())) | |
584 | (match in | |
585 | (() | |
586 | (reverse out)) | |
587 | (((category (name . value) ...) . in) | |
588 | (lp in | |
589 | (cons (make-gl-enumeration | |
590 | category | |
591 | (filter-map | |
592 | (lambda (name value) | |
593 | (and=> (resolve-value category name value) | |
594 | (lambda (value) | |
595 | (cons name value)))) | |
596 | name value)) | |
597 | out)))))) | |
598 | ||
599 | (define (merge-alists in) | |
600 | ;; O(n^2), whee | |
601 | (define (collect-values key values in) | |
602 | (let lp ((in in) (values values)) | |
603 | (if (null? in) | |
604 | values | |
605 | (lp (cdr in) | |
606 | (if (eq? (caar in) key) | |
607 | (append values (cdar in)) | |
608 | values))))) | |
609 | (let lp ((in in) (out '())) | |
610 | (cond | |
611 | ((null? in) (reverse out)) | |
612 | ((assq (caar in) out) (lp (cdr in) out)) | |
613 | (else (lp (cdr in) | |
614 | (acons (caar in) | |
615 | (collect-values (caar in) (cdar in) (cdr in)) | |
616 | out)))))) | |
617 | ||
618 | (define (parse-enumerations-from-port port) | |
619 | (define (finish-block headers enums accum) | |
620 | (if (null? enums) | |
621 | accum | |
622 | (fold (lambda (header accum) | |
623 | (acons header (reverse enums) accum)) | |
624 | accum | |
625 | headers))) | |
626 | (let lp ((current-headers '()) (current-enums '()) (accum '())) | |
627 | (let ((line (read-line-and-trim-comment port))) | |
628 | (cond | |
629 | ((eof-object? line) | |
630 | (resolve-enumerations | |
631 | (merge-alists | |
632 | (reverse (finish-block current-headers current-enums accum))))) | |
633 | ((string-index line #\:) | |
634 | => (lambda (pos) | |
635 | (let* ((ws (or (string-index-right line char-whitespace? 0 pos) 0)) | |
636 | (headers (map string-trim-both | |
637 | (string-split (substring line 0 ws) #\,))) | |
638 | (def (substring line (1+ ws) pos))) | |
639 | (match (cons def headers) | |
640 | (("define" headers ...) | |
641 | (lp '() | |
642 | '() | |
643 | (finish-block current-headers current-enums accum))) | |
644 | (("enum" headers ...) | |
645 | (if (null? current-enums) | |
646 | (lp (append current-headers (map mangle-name headers)) | |
647 | current-enums | |
648 | accum) | |
649 | (lp (map mangle-name headers) | |
650 | '() | |
651 | (finish-block current-headers current-enums accum)))) | |
652 | (x (error "qux." x)))))) | |
653 | ((string-null? line) | |
654 | (lp current-headers current-enums accum)) | |
655 | (else | |
656 | (match (filter (compose not string-null?) | |
657 | (string-split (trim-comment line) char-whitespace?)) | |
658 | ((enum "=" value) | |
659 | (lp current-headers | |
660 | (acons (mangle-name enum) | |
661 | (or (parse-number value) | |
662 | (error "failed to parse" value)) | |
663 | current-enums) | |
664 | accum)) | |
665 | (("use" header enum) | |
666 | (lp current-headers | |
667 | (acons (mangle-name enum) | |
668 | (mangle-name header) | |
669 | current-enums) | |
670 | accum)) | |
671 | (x (error x)))))))) | |
672 | ||
673 | (define (parse-gl-enumerations spec) | |
674 | (call-with-input-file (in-vicinity (upstream-doc) | |
675 | (in-vicinity "spec" spec)) | |
676 | parse-enumerations-from-port)) |