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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
78 79
80 81 82 (defclass VedaPackageAbstractInfo ()
83 ( 84 85 86 87 (name
88 @initarg name
89 @reader GetName)
90 91 92 93 (description
94 @initarg description
95 @reader GetDescription)
96 97 98 99 100 101 102 (authors
103 @initarg authors
104 @reader GetAuthors)))
105
106 107 108 (defclass VedaPackageFilesystemInfo (VedaPackageAbstractInfo)
109 ( 110 111 112 113 (base
114 @initarg base
115 @reader GetBaseDir)
116 117 118 119 (header
120 @initarg header
121 @reader GetHeaderFilename)
122 123 124 125 126 127 128 (requires
129 @initarg requires
130 @reader GetPackageRequires)
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 (sources
148 @initarg sources
149 @reader GetSourceFilenames)
150 151 152 153 (tests
154 @initarg tests
155 @reader GetTestFilenames)))
156
157 158 159 (defmethod printObject ((pi VedaPackageFilesystemInfo) @optional (port poport))
160 (fprintf port "#<%s %L; base: %L>"
161 (className (classOf pi)) pi->name pi->base))
162
163 164 165 (defclass VedaPackageSearchOptions ()
166 ( 167 168 169 170 171 172 173 (searchPath
174 @initarg searchPath
175 @initform '("pkg/" "../")
176 @reader GetSearchPath
177 @writer SetSearchPath)
178 179 180 181 182 183 (asyncHandler
184 @initarg asyncHandler
185 @initform nil
186 @reader GetAsyncHandler
187 @writer SetAsyncHandler)))
188
189 190 191 (defclass VedaPackageLoadOptions (VedaPackageSearchOptions)
192 ( 193 194 195 196 197 198 199 (requires
200 @initarg requires
201 @initform t
202 @reader GetRequires
203 @writer SetRequires)
204 205 206 207 208 209 210 211 212 (forceReload
213 @initarg forceReload
214 @initform nil
215 @reader GetForceReload
216 @writer SetForceReload)))
217
218 219
220 (define VedaPackageInfo nil)
221 (define VedaPackageLoad nil)
222
223 224 (let ()
225 226 (define defaultOptions
227 (makeInstance 'VedaPackageLoadOptions))
228
229 230
231 232
233 234 235 236 237 238 239 240 241 (defun TrimLeftIndex (characterBag string from to)
242 (while (and (from <= to)
243 (nindex characterBag (getchar string from)))
244 (setq from (from + 1)))
245 from)
246
247 248 249 250 251 (defun TrimLeft (characterBag string)
252 (let ((a (TrimLeftIndex characterBag string 1 (strlen string))))
253 (cond
254 ((onep a)
255 string)
256 ((a > (strlen string))
257 "")
258 (t
259 (substring string a)))))
260
261 262 263 264 265 266 267 268 269 (defun TrimRightIndex (characterBag string from to)
270 (while (and (from <= to)
271 (nindex characterBag (getchar string to)))
272 (setq to (to - 1)))
273 to)
274
275 276 277 278 279 (defun TrimRight (characterBag string)
280 (letseq ((n (strlen string))
281 (b (TrimRightIndex characterBag string 1 n)))
282 (if (equal b n)
283 string
284 (substring string 1 b))))
285
286 287 288 (defun PrefixP (needle haystack)
289 (let ((n (strlen needle)))
290 (when (n <= (strlen haystack))
291 (let ((i 1))
292 (while (and i (i <= n))
293 (setq i (when (eq (getchar needle i) (getchar haystack i))
294 (i + 1))))
295 i))))
296
297 298 299 300 (defun SuffixP (needle haystack)
301 (let ((n (strlen needle))
302 (m (strlen haystack)))
303 (when (n <= m)
304 (let ((i 1)
305 (offset (m - n)))
306 (while (and i (i <= n))
307 (setq i (when (eq (getchar needle i)
308 (getchar haystack (offset + i)))
309 (i + 1))))
310 (when i
311 offset)))))
312
313 314
315 316 (define minAtTagLength
317 4)
318
319 320 321 (define knownTags
322 '((nil header)
323 ("@description" description)
324 ("@language" language)
325 ("@load" load)
326 ("@requires" requires)
327 ("@author" authors)
328 (nil tags)))
329
330 331 (define whitespaceBag
332 " \t")
333
334 335 336 (define trailingWhitespaceBag
337 (strcat "\n" whitespaceBag))
338
339 340 341 342 (defun TrimWs1 (line from n)
343 (letseq ((a (TrimLeftIndex whitespaceBag line from (min (from + 1) n)))
344 (b (TrimRightIndex trailingWhitespaceBag line a n)))
345 (cond
346 ((a > b)
347 "")
348 (t
349 (substring line a (b - a + 1))))))
350
351 352 353 354 355 356 (defun TrimLineComment (line)
357 (letseq ((n (strlen line))
358 (ic (TrimLeftIndex ";" line 1 n)))
359 (when (ic > 1)
360 (TrimWs1 line ic n))))
361
362 363 364 365 366 367 368 (defun TrimBlockComment (line)
369 (letseq ((n (strlen line))
370 (iw (TrimLeftIndex whitespaceBag line 1 n))
371 (is (TrimLeftIndex "*" line iw (min (iw + 1) n))))
372 (if (equal iw is)
373 (TrimRight trailingWhitespaceBag line)
374 (TrimWs1 line is n))))
375
376 377 378 (define trimmers
379 `((";" ,TrimLineComment)
380 ("/*" ,TrimBlockComment)))
381
382 383 384 (defun SelectTrimmer (line)
385 (cadar (exists trimmer trimmers
386 (PrefixP (car trimmer) line))))
387
388 389 390 391 392 393 394 395 396 397 398 (defun ParseDescription (line filename)
399 400 (let ((pieces (parseString line " \t\n")))
401 (when (and (member (car pieces) '(";;;" "/*"))
402 (equal (cadr pieces) filename)
403 (equal (caddr pieces) "---"))
404 (buildString (nthcdr 3 pieces)))))
405
406 407 408 409 410 411 (defun SplitTag (rawtag)
412 (let ((i minAtTagLength)
413 (n (strlen rawtag)))
414 415 (while (and (i <= n)
416 (not (nindex whitespaceBag (getchar rawtag i))))
417 (setq i (i + 1)))
418 (if (i > n)
419 (list rawtag "")
420 (list (substring rawtag 1 (i - 1))
421 (substring rawtag (i + 1))))))
422
423 424 425 (defun MakeHeaderDpl (header description languagep tags)
426 (let ((kvs (makeTable 'knownTags nil)))
427 (setarray kvs 'header header)
428 (setarray kvs 'description description)
429 (foreach tag tags
430 (let ((key (cadr (assoc (car tag) knownTags)))
431 (raw (cadr tag)))
432 (cond
433 ((and (eq key 'language) languagep)
434 (setarray kvs key (concat (TrimLeft whitespaceBag raw))))
435 ((memq key '(load requires))
436 (setarray kvs key (parseString raw ", \t")))
437 ((eq key 'authors)
438 (setarray kvs key (cons (TrimLeft whitespaceBag raw)
439 (arrayref kvs key))))
440 ((eq key 'description)
441 (setarray kvs key (TrimLeft whitespaceBag raw)))
442 (t
443 (let ((key 'tags))
444 (setarray kvs key (cons tag (arrayref kvs key))))))))
445 (cons nil
446 (foreach mapcan entry knownTags
447 (letseq ((key (cadr entry))
448 (cooked (arrayref kvs key)))
449 (when cooked
450 (list key cooked)))))))
451
452 453 454 455 456 457 458 459 (defun ParseHeader (liner @key mustDescribe allowShebangP languagep)
460 (let ((line nil)
461 (lineno 0)
462 (trimmer nil)
463 (trimmed nil)
464 (description nil)
465 (tags nil)
466 (tag nil)
467 (it nil)
468 (donep nil))
469 (while (and (not donep) (setq line (funcall liner)))
470 (setq lineno (lineno + 1))
471 472 473 (when (and (eq trimmer TrimBlockComment)
474 (setq it (nindex line "*/")))
475 (setq line (substring line 1 (it - 1)))
476 (setq donep t))
477 (cond
478 ((equal line "\n")
479 480 (setq tag nil))
481 ((and allowShebangP (onep lineno) (PrefixP "#!" line))
482 483 )
484 ((null (or trimmer (setq trimmer (SelectTrimmer line))))
485 486 (setq donep 'fail))
487 ((or (null (setq trimmed (funcall trimmer line)))
488 (member trimmed '("Commentary:" "Code:")))
489 490 491 (setq donep t))
492 ((and mustDescribe (null description)
493 (not (setq description (ParseDescription line mustDescribe))))
494 495 496 (setq donep 'fail))
497 ((and (PrefixP "@" trimmed)
498 ((strlen trimmed) >= minAtTagLength))
499 (setq tag (SplitTag trimmed))
500 (push tag tags))
501 ((and tag (nequal trimmed ""))
502 503 (let ((v (strcat (cadr tag) " " (TrimLeft " \t" trimmed))))
504 (rplaca (cdr tag) v)))
505 (t
506 (setq tag nil))))
507 (unless (eq donep 'fail)
508 (MakeHeaderDpl mustDescribe description languagep tags))))
509
510 511
512 513 514 515 516 517 518 (defun BasicParseHeader (liner filename)
519 (when liner
520 (prog1
521 (ParseHeader liner ?mustDescribe filename)
522 523 (funcall liner t))))
524
525 526 527 528 (defun MaybeFileLiner (path)
529 (let ((port (infile path))
530 (line nil))
531 (when port
532 (lambda (@optional closep)
533 (cond
534 ((not closep)
535 (gets line port))
536 (t
537 (close port)
538 nil))))))
539
540 541 542 543 (defun FilesLoader (filenames @key dir)
544 (lambda (@rest _ignored)
545 (foreach filename filenames
546 (when dir
547 (setq filename (strcat dir filename)))
548 (load filename))
549 t))
550
551 552 553 (define ellipsis
554 "...")
555
556 557 558 559 560 (defun ProcessLoadDirective (directive files)
561 (let ((files (copy files))
562 (ellipsisp nil))
563 (let ((loads (foreach maplist dtail directive
564 (let ((file (car dtail)))
565 (cond
566 ((let ((ftail (member file files)))
567 (when ftail
568 (rplaca ftail nil)
569 file)))
570 ((and (equal file ellipsis)
571 (null (cdr dtail)))
572 (setq ellipsisp t)
573 nil)
574 (t
575 (error "No such file: %L; Load header: %L"
576 file directive)))))))
577 (list (remove nil loads) ellipsisp (remove nil files)))))
578
579 580 581 582 583 (defun ClassifyFiles (files subsets)
584 (let ((dpl (list nil)))
585 (foreach file files
586 (exists subset subsets
587 (let ((suffixes (car subset)))
588 (exists suffix suffixes
589 (when (SuffixP suffix file)
590 (letseq ((key (cadr subset))
591 (accum (get dpl key)))
592 (putprop dpl (cons file accum) key)))))))
593 dpl))
594
595 596 (define fileSubsets
597 '( 598 (("-test.ils" "-test.il") tests)
599 600 601 ((".ils" ".il") sources)))
602
603 604 (defun FinalizePackageInfo (base pkg headerInfo loads ellipsisp files)
605 (letseq ((filesInfo (ClassifyFiles files fileSubsets))
606 (sources (cond
607 ((and loads ellipsisp)
608 (append loads filesInfo->sources))
609 (loads)
610 (t
611 filesInfo->sources))))
612 (makeInstance 'VedaPackageFilesystemInfo
613 ?name pkg
614 ?description headerInfo->description
615 ?authors headerInfo->authors
616 ?base base
617 ?header headerInfo->header
618 ?requires headerInfo->requires
619 ?sources sources
620 ?tests filesInfo->tests)))
621
622 623 624 625 626 (defun PackageInfoFromBase (base pkg headerInfo files)
627 (apply FinalizePackageInfo base pkg headerInfo
628 (if headerInfo->load
629 (ProcessLoadDirective headerInfo->load files)
630 (list nil t files))))
631
632 633 634 (defun PackageInfoHelper (pkg bases suffixes)
635 (let (info)
636 (exists base bases
637 (let ((dir (strcat base pkg)))
638 (exists suffix suffixes
639 (letseq ((filename (strcat pkg suffix))
640 (liner (MaybeFileLiner (strcat dir "/" filename)))
641 (headerInfo (BasicParseHeader liner filename)))
642 (when headerInfo
643 (let ((files (getDirFiles dir)))
644 (setq info (PackageInfoFromBase base pkg headerInfo
645 files))))))))
646 info))
647
648 649 650 651 652 653 (defun PackageInfoAsyncHelper (handler pkg bases suffixes k)
654
655 (defun IterSuffixes (base suffixes k)
656 (if (null suffixes)
657 (k nil)
658 (letseq ((filename (strcat pkg (car suffixes)))
659 (path (strcat base pkg "/" filename)))
660 (VedaPackageAsyncMakeLiner handler path
661 (lambda (liner)
662 (let ((info (BasicParseHeader liner filename)))
663 (if info
664 (k info)
665 (IterSuffixes base (cdr suffixes) k))))))))
666
667 (defun IterBases (bases k)
668 (if (null bases)
669 (k nil nil)
670 (IterSuffixes (car bases) suffixes
671 (lambda (info)
672 (if info
673 (k (car bases) info)
674 (IterBases (cdr bases) k))))))
675
676 (defun Files (base info files)
677 (let ((packageInfo (when files
678 (PackageInfoFromBase base pkg info files))))
679 (k handler packageInfo)))
680
681 (defun HeaderInfo (base info)
682 (cond
683 ((null info)
684 (k handler nil))
685 (t
686 (let ((dir (strcat base pkg "/")))
687 (VedaPackageAsyncGetDirFiles handler dir
688 (lambda (files)
689 (Files base info files)))))))
690
691 (IterBases bases HeaderInfo))
692
693 694 695 696 697 698 699 700 701 702 703 704 (defun PackageInfoFromFilesystem (pkg bases asyncHandler)
705 (let ((suffixes '(".il" ".ils")))
706 (cond
707 ((null asyncHandler)
708 (PackageInfoHelper pkg bases suffixes))
709 (t
710 (PackageInfoAsyncHelper asyncHandler
711 pkg bases suffixes
712 VedaPackageAsyncResult)
713 nil))))
714
715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 (defun PackageInfo (pkg @key options)
737 (cond
738 ((isCallable 'VedaPackageInfoHook)
739 (funcall 'VedaPackageInfoHook pkg))
740 (t
741 (let ((options (or options defaultOptions)))
742 (PackageInfoFromFilesystem pkg
743 (GetSearchPath options)
744 (GetAsyncHandler options))))))
745
746 747 748 749 (defun LoaderFromPackageInfo (info subset)
750 (let (files)
751 (when (setq files (get info subset))
752 (let ((dir (strcat info->base info->name "/")))
753 (FilesLoader files ?dir dir)))))
754
755 756 757 (defun LoadSubsetFromPackageInfo (info subset)
758 (let ((loader (LoaderFromPackageInfo info subset)))
759 (if loader
760 (funcall loader ?subset subset)
761 (error "Don't know how to load %s from package %L."
762 subset info)))
763 t)
764
765 766 (defun RequirePackageRec (pkg loaded options noPackageRequiresP)
767 (let (info)
768 (cond
769 ((arrayref loaded pkg))
770 ((setq info (PackageInfo pkg ?options options))
771 (unless noPackageRequiresP
772 (foreach require info->requires
773 (RequirePackageRec require loaded options noPackageRequiresP)))
774 (LoadSubsetFromPackageInfo info 'sources)
775 (setarray loaded pkg info))
776 (t
777 (error "Package %L not found." pkg)))))
778
779 780 781 782 783 784 (defun RequirePackage (pkg loaded options)
785 (letseq ((requires (GetRequires options))
786 (noPackageRequiresP (neq requires t)))
787 (when (pairp requires)
788 (foreach require requires
789 (RequirePackageRec require loaded options noPackageRequiresP)))
790 (RequirePackageRec pkg loaded options noPackageRequiresP)))
791
792 793 794 795 (defun BootstrapPackageInfo (filename)
796 (when (SuffixP ".ils" filename)
797 (let ((n (strlen filename))
798 (suffix (rindex filename "/")))
799 (cond
800 (suffix
801 (let ((slen (strlen suffix)))
802 (let ((pkg (substring suffix 2 (slen - 5)))
803 (dir (strcat (substring filename 1 (n - slen + 1)) "../")))
804 (PackageInfoFromFilesystem pkg (list dir) nil))))
805 (t
806 (let ((pkg (substring filename 1 (n - 4))))
807 (PackageInfoFromFilesystem pkg '("../") nil)))))))
808
809 810 811 812 (defun BootstrapMarkLoaded (table filename)
813 (when filename
814 (let ((info (BootstrapPackageInfo filename)))
815 (when info
816 (setarray table (GetName info) info)))))
817
818 819 (define loadedTable nil)
820
821 (let ((queryFn 'VedaPackage__getLoadedTable))
822 (cond
823 ((isCallable queryFn)
824 (setq loadedTable (funcall queryFn)))
825 (t
826 (setq loadedTable (makeTable 'loaded nil))
827 (putd queryFn (lambda () loadedTable)))))
828
829 830 (when (isCallable 'get_filename)
831 (BootstrapMarkLoaded loadedTable (get_filename piport)))
832
833 834 835 836 (defun CleanupLoadedTable (forceReload)
837 (let ((pkgs (cond
838 ((eq forceReload t)
839 loadedTable->?)
840 ((pairp forceReload)
841 forceReload)
842 (t
843 (error "Unrecognized force reload option %L."
844 forceReload)))))
845 (foreach pkg pkgs
846 (remove pkg loadedTable))))
847
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 (defun PackageLoad (pkg @key options)
867 (letseq ((options (or options defaultOptions))
868 (forceReload (GetForceReload options)))
869 (when forceReload
870 (CleanupLoadedTable forceReload))
871 (RequirePackage pkg loadedTable options)))
872
873 (setq VedaPackageInfo PackageInfo)
874 (setq VedaPackageLoad PackageLoad))
875
876