Source File package/package.ils
1 2
3 4
5 6
7 8 9 10 11 12 13 14
15 16
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
52 53
54 55 56 (defclass VedaPackageAbstractInfo ()
57 ( 58 59 (name
60 @initarg name
61 @reader GetName)
62 63 (description
64 @initarg description
65 @reader GetDescription)
66 67 68 69 70 (authors
71 @initarg authors
72 @reader GetAuthors)))
73
74 75 76 (defclass VedaPackageFilesystemInfo (VedaPackageAbstractInfo)
77 ( 78 79 (base
80 @initarg base
81 @reader GetBaseDir)
82 83 (header
84 @initarg header
85 @reader GetHeaderFilename)
86 87 88 89 90 (requires
91 @initarg requires
92 @reader GetPackageRequires)
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 (sources
108 @initarg sources
109 @reader GetSourceFilenames)
110 111 (tests
112 @initarg tests
113 @reader GetTestFilenames)))
114
115 116 117 (defmethod printObject ((pi VedaPackageFilesystemInfo) @optional (port poport))
118 (fprintf port "#<%s %L; base: %L>"
119 (className (classOf pi)) pi->name pi->base))
120
121 122 123 (defclass VedaPackageSearchOptions ()
124 ( 125 126 127 128 129 (searchPath
130 @initarg searchPath
131 @initform '("pkg/" "../")
132 @reader GetSearchPath
133 @writer SetSearchPath)))
134
135 136 137 (defclass VedaPackageLoadOptions (VedaPackageSearchOptions)
138 ( 139 140 141 142 143 (requires
144 @initarg requires
145 @initform t
146 @reader GetRequires
147 @writer SetRequires)
148 149 150 151 152 153 154 (forceReload
155 @initarg forceReload
156 @initform nil
157 @reader GetForceReload
158 @writer SetForceReload)))
159
160 161 (let ()
162 163 164 (defun Export (suffix fn)
165 (putd (concat 'VedaPackage suffix) fn))
166
167 168 (define defaultOptions
169 (makeInstance 'VedaPackageLoadOptions))
170
171 172 173 174 (defun FilesLoader (filenames @key dir)
175 (lambda (@rest _ignored)
176 (foreach filename filenames
177 (when dir
178 (setq filename (strcat dir filename)))
179 (load filename))
180 t))
181
182 183 184 185 186 187 (defun Trim (characterBag string @key fromEndP)
188 (let ((a 1)
189 (n (strlen string)))
190 (let ((b n)
191 (i (if fromEndP n a)))
192 (while (and (a <= b)
193 (nindex characterBag (getchar string i)))
194 (setq i (if fromEndP
195 (setq b (b - 1))
196 (setq a (a + 1)))))
197 (cond
198 ((and (onep a) (equal b n))
199 string)
200 (t
201 (substring string a (b - a + 1)))))))
202
203 204 205 (define lineCommentTrimBag
206 "; \t")
207
208 209 210 (define blockCommentTrimBag
211 "* \t")
212
213 214 215 216 217 218 219 220 221 222 223 224 225 (defun ParseDescriptionLine (line filename)
226 227 (letseq ((pieces (parseString line " \t\n"))
228 (bag (cond
229 ((equal (car pieces) ";;;") lineCommentTrimBag)
230 ((equal (car pieces) "/*") blockCommentTrimBag))))
231 (when (and bag
232 (equal (cadr pieces) filename)
233 (equal (caddr pieces) "---"))
234 (list bag (buildString (nthcdr 3 pieces))))))
235
236 237 238 (defun PrefixP (needle haystack)
239 (let ((n (strlen needle)))
240 (when (n <= (strlen haystack))
241 (let ((i 1))
242 (while (and i (i <= n))
243 (setq i (when (eq (getchar needle i) (getchar haystack i))
244 (i + 1))))
245 i))))
246
247 248 249 250 (defun SuffixP (needle haystack)
251 (let ((n (strlen needle))
252 (m (strlen haystack)))
253 (when (n <= m)
254 (let ((i 1)
255 (offset (m - n)))
256 (while (and i (i <= n))
257 (setq i (when (eq (getchar needle i)
258 (getchar haystack (offset + i)))
259 (i + 1))))
260 (when i
261 offset)))))
262
263 264 265 266 (defun TrimMark (trimBag line mark)
267 (letseq ((trimmed (Trim trimBag line))
268 (i (PrefixP mark trimmed)))
269 (when i
270 (substring trimmed i))))
271
272 273 274 275 276 277 (defun ParseHeader (path filename)
278 (let ((port (infile path)))
279 (when port
280 (let ((info (list nil 'header filename))
281 (authors nil)
282 (line nil)
283 (desc nil)
284 (it nil)
285 (donep nil))
286 (while (and (not donep) (gets line port))
287 (cond
288 ((equal line "\n")
289 290 )
291 ((not (or desc (setq desc (ParseDescriptionLine line filename))))
292 (setq info nil)
293 (setq donep nil))
294 295 ((setq it (TrimMark (car desc) line "@load"))
296 (info->load = (parseString it ", \t\n")))
297 ((setq it (TrimMark (car desc) line "@requires"))
298 (info->requires = (parseString it ", \t\n")))
299 ((setq it (TrimMark (car desc) line "@author"))
300 (letseq ((s1 (Trim " \t\n" it))
301 (s2 (Trim " \t\n" s1 ?fromEndP t)))
302 (push s2 authors)))
303 304 305 ((or (TrimMark (car desc) line "Commentary:")
306 (TrimMark (car desc) line "Code:")
307 (when (eq (car desc) blockCommentTrimBag)
308 (nindex line "*/")))
309 (setq donep t))))
310 (close port)
311 (when info
312 (info->description = (cadr desc))
313 (info->authors = (reverse authors))
314 info)))))
315
316 317 318 (define ellipsis
319 "...")
320
321 322 323 324 325 (defun ProcessLoadDirective (directive files)
326 (let ((files (copy files))
327 (ellipsisp nil))
328 (let ((loads (foreach maplist dtail directive
329 (let ((file (car dtail)))
330 (cond
331 ((let ((ftail (member file files)))
332 (when ftail
333 (rplaca ftail nil)
334 file)))
335 ((and (equal file ellipsis)
336 (null (cdr dtail)))
337 (setq ellipsisp t)
338 nil)
339 (t
340 (error "No such file: %L; Load header: %L"
341 file directive)))))))
342 (list (remove nil loads) ellipsisp (remove nil files)))))
343
344 345 346 347 348 (defun ClassifyFiles (files subsets)
349 (let ((dpl (list nil)))
350 (foreach file files
351 (exists subset subsets
352 (let ((suffixes (car subset)))
353 (exists suffix suffixes
354 (when (SuffixP suffix file)
355 (letseq ((key (cadr subset))
356 (accum (get dpl key)))
357 (putprop dpl (cons file accum) key)))))))
358 dpl))
359
360 361 (define fileSubsets
362 '( 363 (("-test.ils" "-test.il") tests)
364 365 366 ((".ils" ".il") sources)))
367
368 369 (defun FinalizePackageInfo (base pkg headerInfo loads ellipsisp files)
370 (letseq ((filesInfo (ClassifyFiles files fileSubsets))
371 (sources (cond
372 ((and loads ellipsisp)
373 (append loads filesInfo->sources))
374 (loads)
375 (t
376 filesInfo->sources))))
377 (makeInstance 'VedaPackageFilesystemInfo
378 ?name pkg
379 ?description headerInfo->description
380 ?authors headerInfo->authors
381 ?base base
382 ?header headerInfo->header
383 ?requires headerInfo->requires
384 ?sources sources
385 ?tests filesInfo->tests)))
386
387 388 389 390 391 (defun PackageInfoFromBase (base pkg headerInfo)
392 (letseq ((dir (strcat base pkg "/"))
393 (files (getDirFiles dir)))
394 (apply FinalizePackageInfo base pkg headerInfo
395 (if headerInfo->load
396 (ProcessLoadDirective headerInfo->load files)
397 (list nil t files)))))
398
399 400 401 402 403 404 405 (defun PackageInfoFromFilesystem (pkg bases)
406 (let (info)
407 (exists base bases
408 (let ((dir (strcat base pkg)))
409 (exists suffix '(".il" ".ils")
410 (letseq ((filename (strcat pkg suffix))
411 (path (strcat dir "/" filename))
412 (headerInfo (ParseHeader path filename)))
413 (when headerInfo
414 (setq info (PackageInfoFromBase base pkg headerInfo)))))))
415 info))
416
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 (defun PackageInfo (pkg @key options)
435 (cond
436 ((isCallable 'VedaPackageInfoHook)
437 (funcall 'VedaPackageInfoHook pkg))
438 (t
439 (let ((options (or options defaultOptions)))
440 (PackageInfoFromFilesystem pkg (GetSearchPath options))))))
441
442 (Export 'Info PackageInfo)
443
444 445 446 447 (defun LoaderFromPackageInfo (info subset)
448 (let (files)
449 (when (setq files (get info subset))
450 (let ((dir (strcat info->base info->name "/")))
451 (FilesLoader files ?dir dir)))))
452
453 454 455 (defun LoadSubsetFromPackageInfo (info subset)
456 (let ((loader (LoaderFromPackageInfo info subset)))
457 (if loader
458 (funcall loader ?subset subset)
459 (error "Don't know how to load %s from package %L."
460 subset info)))
461 t)
462
463 464 (defun RequirePackageRec (pkg loaded options noPackageRequiresP)
465 (let (info)
466 (cond
467 ((arrayref loaded pkg))
468 ((setq info (PackageInfo pkg ?options options))
469 (unless noPackageRequiresP
470 (foreach require info->requires
471 (RequirePackageRec require loaded options noPackageRequiresP)))
472 (LoadSubsetFromPackageInfo info 'sources)
473 (setarray loaded pkg info))
474 (t
475 (error "Package %L not found." pkg)))))
476
477 478 479 480 481 482 (defun RequirePackage (pkg loaded options)
483 (letseq ((requires (GetRequires options))
484 (noPackageRequiresP (neq requires t)))
485 (when (pairp requires)
486 (foreach require requires
487 (RequirePackageRec require loaded options noPackageRequiresP)))
488 (RequirePackageRec pkg loaded options noPackageRequiresP)))
489
490 491 492 493 494 (defun MakeLoadedTable (preloaded)
495 (let ((loaded (makeTable 'loaded nil)))
496 (foreach pkg preloaded
497 (let ((info (PackageInfo pkg)))
498 (setarray loaded pkg info)))
499 loaded))
500
501 502 (define defaultLoadedTable
503 (MakeLoadedTable '("package")))
504
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 (defun PackageLoad (pkg @key options)
520 (letseq ((options (or options defaultOptions))
521 (forceReload (GetForceReload options)))
522 (cond
523 ((eq forceReload t)
524 (setq defaultLoadedTable
525 (MakeLoadedTable nil)))
526 ((pairp forceReload)
527 (foreach pkg forceReload
528 (remove pkg defaultLoadedTable))))
529 (RequirePackage pkg defaultLoadedTable options)))
530
531 (Export 'Load PackageLoad))
532
533