Skip to content

Commit e620354

Browse files
committed
Add 'lisp-toplevel' form to coalton syntax
lisp-toplevel may appear at any point in a coalton-toplevel or coalton file context. During code generation, lisp forms are merged with coalton definitions according to the rule: 1. coalton forms are emitted in scc order 2. every time a coalton form is emitted, all lisp forms lexically preceding that form are emitted first Convert math/num.lisp from interleaved lisp and coalton-toplevel forms to a single coalton-toplevel form containing multiple lisp-toplevel forms.
1 parent ab637bb commit e620354

File tree

8 files changed

+540
-399
lines changed

8 files changed

+540
-399
lines changed

Diff for: library/math/num.lisp

+334-323
Large diffs are not rendered by default.

Diff for: src/codegen/program.lisp

+91-33
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#:optimize-bindings)
2323
(:local-nicknames
2424
(#:util #:coalton-impl/util)
25+
(#:parser #:coalton-impl/parser)
2526
(#:settings #:coalton-impl/settings)
2627
(#:global-lexical #:coalton-impl/global-lexical)
2728
(#:rt #:coalton-impl/runtime)
@@ -41,41 +42,104 @@ A function bound here will be called with a keyword category, and one or more ad
4142
4243
Toplevel definitions, after type checking and before compilation.")
4344

45+
;; The following functions control the output order of compiled
46+
;; definitions and interleaved lisp expressions.
47+
;;
48+
;; Toplevel define and instance forms are compiled to 1 or more named,
49+
;; lisp-source-valued output definitions: when these definitions are
50+
;; generated, they are associated with the starting source offset of their
51+
;; toplevel form:
52+
;;
53+
;; toplevel definition:
54+
;; #<def .... (300 . 345)>
55+
;;
56+
;; bindings (lisp definitions):
57+
;; (300 b1 .. bn)
58+
;;
59+
;; Then when compile-definitions emits the full set of output
60+
;; definitions, any lisp source forms that occurred earlier in the
61+
;; file are emitted first.
62+
63+
(defun bindings-offset (bindings offsets)
64+
"Given a list of binding names, and a name -> offset map, return the earliest binding start offset."
65+
(apply #'min
66+
(mapcar (lambda (binding)
67+
(or (gethash (car binding) offsets) 0))
68+
bindings)))
69+
70+
(defun merge-forms (forms-a forms-b &optional merged)
71+
"Stably merge two lists of forms.
72+
73+
1. The inputs and output are lists of 2-lists, structured as (OFFSET FORM).
74+
2. The order of both lists is preserved.
75+
3. Lists are merged by recursively selecting the head of the list with the lowest offset."
76+
(cond ((endp forms-a)
77+
(return-from merge-forms
78+
(append (nreverse merged) forms-b)))
79+
((endp forms-b)
80+
(return-from merge-forms
81+
(append (nreverse merged) forms-a)))
82+
((< (caar forms-a)
83+
(caar forms-b))
84+
(push (pop forms-a) merged))
85+
(t
86+
(push (pop forms-b) merged)))
87+
(merge-forms forms-a forms-b merged))
88+
89+
(defun compile-definitions (sccs definitions lisp-forms offsets env)
90+
"Compile SCCs and generate a final output definition list, merging any present lisp sources."
91+
(let ((bindings (loop :for scc :in sccs
92+
:for bindings := (remove-if-not (lambda (binding)
93+
(find (car binding) scc))
94+
definitions)
95+
:collect (cons (bindings-offset bindings offsets)
96+
(compile-scc bindings env))))
97+
(lisp-forms (mapcar (lambda (lisp-form)
98+
(cons (car (parser:toplevel-lisp-form-source lisp-form))
99+
(parser:toplevel-lisp-form-body lisp-form)))
100+
lisp-forms)))
101+
(mapcan #'cdr (merge-forms bindings lisp-forms))))
102+
103+
(defun definition-bindings (definitions env offsets)
104+
(loop :for define :in definitions
105+
:for offset := (car (tc:toplevel-define-source define))
106+
:for name := (tc:node-variable-name (tc:toplevel-define-name define))
107+
:for compiled-node := (translate-toplevel define env)
108+
109+
:when *codegen-hook*
110+
:do (funcall *codegen-hook* :ast
111+
name
112+
(tc:lookup-value-type env name)
113+
(tc:binding-value define))
114+
115+
:collect (cons name compiled-node)))
116+
117+
(defun instance-bindings (instances env offsets)
118+
(loop :for instance :in instances
119+
:for offset := (car (tc:toplevel-define-instance-source instance))
120+
:for instance-bindings := (translate-instance instance env)
121+
122+
:do (dolist (binding instance-bindings)
123+
(setf (gethash (car binding) offsets) offset))
124+
:append instance-bindings))
125+
44126
(defun compile-translation-unit (translation-unit monomorphize-table env)
45127
(declare (type tc:translation-unit translation-unit)
46128
(type hash-table monomorphize-table)
47129
(type tc:environment env))
48130

49-
(let* ((definitions
131+
(let* ((offsets (make-hash-table))
132+
(definitions
50133
(append
51-
(loop :for define :in (tc:translation-unit-definitions translation-unit)
52-
:for name := (tc:node-variable-name (tc:toplevel-define-name define))
53-
54-
:for compiled-node := (translate-toplevel define env)
55-
56-
:do (when *codegen-hook*
57-
(funcall *codegen-hook*
58-
':AST
59-
name
60-
(tc:lookup-value-type env name)
61-
(tc:binding-value define)))
62-
:collect (cons name compiled-node))
63-
64-
;; HACK: this load bearing reverse should be replaced with an actual solution
65-
(loop :for instance :in (reverse (tc:translation-unit-instances translation-unit))
66-
:append (translate-instance instance env))))
67-
68-
(definition-names
69-
(mapcar #'car definitions)))
134+
(definition-bindings (tc:translation-unit-definitions translation-unit) env offsets)
135+
(instance-bindings (tc:translation-unit-instances translation-unit) env offsets)))
136+
(definition-names (mapcar #'car definitions)))
70137

71138
(multiple-value-bind (definitions env)
72-
(optimize-bindings
73-
definitions
74-
monomorphize-table
75-
*package*
76-
env)
139+
(optimize-bindings definitions monomorphize-table *package* env)
77140

78-
(let ((sccs (node-binding-sccs definitions)))
141+
(let ((sccs (node-binding-sccs definitions))
142+
(lisp-forms (tc:translation-unit-lisp-forms translation-unit)))
79143

80144
(values
81145
`(progn
@@ -102,13 +166,7 @@ A function bound here will be called with a keyword category, and one or more ad
102166
(list
103167
`(declaim (sb-ext:start-block ,@definition-names))))
104168

105-
,@(loop :for scc :in sccs
106-
:for bindings
107-
:= (remove-if-not
108-
(lambda (binding)
109-
(find (car binding) scc))
110-
definitions)
111-
:append (compile-scc bindings env))
169+
,@(compile-definitions sccs definitions lisp-forms offsets env)
112170

113171
#+sbcl
114172
,@(when (eq sb-ext:*block-compile-default* :specified)

Diff for: src/entry.lisp

+40-37
Original file line numberDiff line numberDiff line change
@@ -43,53 +43,56 @@
4343
file
4444
env)
4545

46-
(multiple-value-bind (class-definitions env)
47-
(tc:toplevel-define-class (parser:program-classes program)
48-
file
49-
env)
46+
(let ((all-instances (append instances (parser:program-instances program))))
47+
48+
(multiple-value-bind (class-definitions env)
49+
(tc:toplevel-define-class (parser:program-classes program)
50+
file
51+
env)
5052

51-
(multiple-value-bind (ty-instances env)
52-
(tc:toplevel-define-instance (append instances (parser:program-instances program)) env file)
53+
(multiple-value-bind (ty-instances env)
54+
(tc:toplevel-define-instance all-instances env file)
5355

54-
(multiple-value-bind (toplevel-definitions env)
55-
(tc:toplevel-define (parser:program-defines program)
56-
(parser:program-declares program)
57-
file
58-
env)
56+
(multiple-value-bind (toplevel-definitions env)
57+
(tc:toplevel-define (parser:program-defines program)
58+
(parser:program-declares program)
59+
file
60+
env)
5961

60-
(multiple-value-bind (toplevel-instances)
61-
(tc:toplevel-typecheck-instance ty-instances
62-
(append instances (parser:program-instances program))
63-
env
64-
file)
62+
(multiple-value-bind (toplevel-instances)
63+
(tc:toplevel-typecheck-instance ty-instances
64+
all-instances
65+
env
66+
file)
6567

66-
(setf env (tc:toplevel-specialize (parser:program-specializations program) env file))
68+
(setf env (tc:toplevel-specialize (parser:program-specializations program) env file))
6769

68-
(let ((monomorphize-table (make-hash-table :test #'eq))
70+
(let ((monomorphize-table (make-hash-table :test #'eq))
6971

70-
(translation-unit
71-
(tc:make-translation-unit
72-
:types type-definitions
73-
:definitions toplevel-definitions
74-
:classes class-definitions
75-
:instances toplevel-instances
76-
:package *package*)))
72+
(translation-unit
73+
(tc:make-translation-unit
74+
:types type-definitions
75+
:definitions toplevel-definitions
76+
:classes class-definitions
77+
:instances toplevel-instances
78+
:lisp-forms (parser:program-lisp-forms program)
79+
:package *package*)))
7780

78-
(loop :for define :in (parser:program-defines program)
79-
:when (parser:toplevel-define-monomorphize define)
80-
:do (setf (gethash (parser:node-variable-name (parser:toplevel-define-name define))
81-
monomorphize-table)
82-
t))
81+
(loop :for define :in (parser:program-defines program)
82+
:when (parser:toplevel-define-monomorphize define)
83+
:do (setf (gethash (parser:node-variable-name (parser:toplevel-define-name define))
84+
monomorphize-table)
85+
t))
8386

84-
(loop :for declare :in (parser:program-declares program)
85-
:when (parser:toplevel-declare-monomorphize declare)
86-
:do (setf (gethash (parser:identifier-src-name (parser:toplevel-declare-name declare))
87-
monomorphize-table)
88-
t))
87+
(loop :for declare :in (parser:program-declares program)
88+
:when (parser:toplevel-declare-monomorphize declare)
89+
:do (setf (gethash (parser:identifier-src-name (parser:toplevel-declare-name declare))
90+
monomorphize-table)
91+
t))
8992

90-
(analysis:analyze-translation-unit translation-unit env file)
93+
(analysis:analyze-translation-unit translation-unit env file)
9194

92-
(codegen:compile-translation-unit translation-unit monomorphize-table env)))))))))
95+
(codegen:compile-translation-unit translation-unit monomorphize-table env))))))))))
9396

9497

9598
(defun expression-entry-point (node file)

Diff for: src/faux-macros.lisp

+3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
(define-coalton-editor-macro coalton:define-instance (instance &body method-definitions)
4646
"Define an instance of a type class. (Coalton top-level operator.)")
4747

48+
(define-coalton-editor-macro coalton:lisp-toplevel (name from-ty to-ty)
49+
"Include lisp forms. (Coalton top-level operator.)")
50+
4851
(define-coalton-editor-macro coalton:specialize (name from-ty to-ty)
4952
"Declare a specialization for a function. (Coalton top-level operator.)")
5053

Diff for: src/package.lisp

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#:define-class
2828
#:define-instance
2929
#:repr
30+
#:lisp-toplevel
3031
#:monomorphize
3132
#:specialize
3233
#:unable-to-codegen)

Diff for: src/parser/renamer.lisp

+1
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@
521521
:defines (rename-variables-generic% (program-defines program) ctx)
522522
:classes (program-classes program) ; Class type variables are renamed during kind inference
523523
:instances (rename-variables-generic% (program-instances program) ctx)
524+
:lisp-forms (program-lisp-forms program)
524525
:specializations (program-specializations program) ; Renaming type variables in specializations is not valid
525526
)
526527
ctx))

0 commit comments

Comments
 (0)