Add Licensing and Contributing
[clnl] / src / main / clnl / extensions.lisp
1 ; Copyright 2022 Frank Duncan (frank@consxy.com) under AGPL3.  See distributed LICENSE.txt.
2 (in-package #:clnl-extensions)
3
4 (defgeneric prims (extension)
5  (:documentation
6   "PRIMS EXTENSION => PRIMS
7
8   PRIMS: PRIM*
9   PRIM: (:name NAME :type TYPE :args ARGS :func FUNC)
10   TYPE: :command | :reporter
11   ARGS: ARG*
12
13 ARGUMENTS AND VALUES:
14
15   EXTENSION: a symbol in the keyword package representing this extension
16   NAME: a symbol in the keyword package
17   FUNC: the function to call
18   ARG: a list of symbols denoting the type of argument
19
20 DESCRIPTION:
21
22   PRIMS returns the primitives used in the extension passed in.
23
24   See CLNL-PARSER:PARSE for more information on the PRIM returned."))
25
26 (defun load-extension (extension)
27  "LOAD-EXTENSION EXTENSION => PRIMS
28
29 ARGUMENTS AND VALUES:
30
31   EXTENSION: A symbol
32   PRIMS: Primitives that can be sent to the parser and transpiler
33
34 DESCRIPTION:
35
36   LOAD-EXTENSION takes an EXTENSION and does the work to load the asdf package,
37   as well as munge the prims from extension style prims to things to be used by
38   the CLNL compiler stack.
39
40   It returns those PRIMS after checking that all the pieces are there to not
41   break the runtime."
42  (let*
43   ((name (intern (format nil "CLNL-EXTENSION-~A" (string-upcase extension)) :keyword)))
44   (asdf:load-system name)
45   (let
46    ((pkg (find-package name)))
47    (when (or (not pkg)) (error "Can't find package with extension name: ~A" name))
48    (when (not (compute-applicable-methods #'prims (list extension)))
49     (error "Can't find implemented PRIMS method within extension: ~A" name))
50    (mapcar
51     (lambda (prim)
52      (when (not (getf prim :name)) (error "Prim requires a name: ~A ~A" name prim))
53      (let
54       ((type (getf prim :type)))
55       (when (or (not type) (not (find type '(:reporter :command))))
56        (error "Prim type invalid, needs to be :reporter or :command: ~A ~A ~A" name prim type)))
57      (when (not (getf prim :func))
58       (error "Prim needs a func: ~A ~A" name prim))
59      (list
60       :name (intern
61              (format nil "~A:~A"
62               (if (eql extension :cli) "" (string-upcase extension))
63               (string-upcase (getf prim :name)))
64              :keyword)
65       :type (getf prim :type)
66       :precedence (or (getf prim :precedence) (if (eql :reporter (getf prim :type)) 10 0))
67       :args (getf prim :args)
68       :func (getf prim :func)))
69     (prims extension)))))