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