genie-call.c
1 //! @file genie-call.c
2 //! @author J. Marcel van der Veer
3
4 //! @section Copyright
5 //!
6 //! This file is part of Algol68G - an Algol 68 compiler-interpreter.
7 //! Copyright 2001-2024 J. Marcel van der Veer [algol68g@xs4all.nl].
8
9 //! @section License
10 //!
11 //! This program is free software; you can redistribute it and/or modify it
12 //! under the terms of the GNU General Public License as published by the
13 //! Free Software Foundation; either version 3 of the License, or
14 //! (at your option) any later version.
15 //!
16 //! This program is distributed in the hope that it will be useful, but
17 //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 //! more details. You should have received a copy of the GNU General Public
20 //! License along with this program. If not, see [http://www.gnu.org/licenses/].
21
22 //! @section Synopsis
23 //!
24 //! Interpreter routines for procedure calls.
25
26 // Algol 68 Genie implements Charles Lindsey's proposal for partial parametrization.
27 // A procedure has a locale to store parameters until the pack is complete, and only
28 // then the procedure is actually called.
29
30 #include "a68g.h"
31 #include "a68g-genie.h"
32 #include "a68g-frames.h"
33 #include "a68g-prelude.h"
34 #include "a68g-transput.h"
35
36 void genie_argument (NODE_T * p, NODE_T ** seq)
37 {
38 for (; p != NO_NODE; FORWARD (p)) {
39 if (IS (p, UNIT)) {
40 GENIE_UNIT_NO_GC (p);
41 STACK_DNS (p, MOID (p), A68_FP);
42 SEQUENCE (*seq) = p;
43 (*seq) = p;
44 return;
45 } else if (IS (p, TRIMMER)) {
46 return;
47 } else {
48 genie_argument (SUB (p), seq);
49 }
50 }
51 }
52
53 //! @brief Evaluate partial call.
54
55 void genie_partial_call (NODE_T * p, MOID_T * p_mode, MOID_T * pproc, MOID_T * pmap, A68_PROCEDURE z, ADDR_T pop_sp, ADDR_T pop_fp)
56 {
57 // Get or make locale for the new procedure descriptor.
58 A68_REF ref; A68_HANDLE *locale;
59 if (LOCALE (&z) == NO_HANDLE) {
60 int size = 0;
61 for (PACK_T *s = PACK (p_mode); s != NO_PACK; FORWARD (s)) {
62 size += (SIZE (M_BOOL) + SIZE (MOID (s)));
63 }
64 ref = heap_generator (p, p_mode, size);
65 locale = REF_HANDLE (&ref);
66 } else {
67 int size = SIZE (LOCALE (&z));
68 ref = heap_generator (p, p_mode, size);
69 locale = REF_HANDLE (&ref);
70 COPY (POINTER (locale), POINTER (LOCALE (&z)), size);
71 }
72 // Move arguments from stack to locale using pmap.
73 BYTE_T *u = POINTER (locale), *v = STACK_ADDRESS (pop_sp);
74 // Uninitialised arguments are VOID.
75 int voids = 0;
76 PACK_T *s = PACK (p_mode);
77 for (PACK_T *t = PACK (pmap); t != NO_PACK && s != NO_PACK; FORWARD (t)) {
78 // Skip already initialised arguments.
79 while (u != NULL && VALUE ((A68_BOOL *) & u[0])) {
80 u = &(u[SIZE (M_BOOL) + SIZE (MOID (s))]);
81 FORWARD (s);
82 }
83 if (u != NULL && MOID (t) == M_VOID) {
84 // Move to next field in locale.
85 voids++;
86 u = &(u[SIZE (M_BOOL) + SIZE (MOID (s))]);
87 FORWARD (s);
88 } else {
89 // Move argument from stack to locale.
90 A68_BOOL w;
91 STATUS (&w) = INIT_MASK;
92 VALUE (&w) = A68_TRUE;
93 *(A68_BOOL *) & u[0] = w;
94 COPY (&(u[SIZE (M_BOOL)]), v, SIZE (MOID (t)));
95 u = &(u[SIZE (M_BOOL) + SIZE (MOID (s))]);
96 v = &(v[SIZE (MOID (t))]);
97 FORWARD (s);
98 }
99 }
100 A68_SP = pop_sp;
101 LOCALE (&z) = locale;
102 // When closure is complete, push locale onto the stack and call procedure body.
103 if (voids == 0) {
104 A68_SP = pop_sp;
105 u = POINTER (locale);
106 v = STACK_ADDRESS (A68_SP);
107 for (s = PACK (p_mode); s != NO_PACK; FORWARD (s)) {
108 int size = SIZE (MOID (s));
109 COPY (v, &u[SIZE (M_BOOL)], size);
110 u = &(u[SIZE (M_BOOL) + size]);
111 v = &(v[SIZE (MOID (s))]);
112 INCREMENT_STACK_POINTER (p, size);
113 }
114 genie_call_procedure (p, p_mode, pproc, M_VOID, &z, pop_sp, pop_fp);
115 } else {
116 // Closure is not complete. Return procedure body.
117 PUSH_PROCEDURE (p, z);
118 }
119 }
120
121 //! @brief Closure and deproceduring of routines with PARAMSETY.
122
123 void genie_call_procedure (NODE_T * p, MOID_T * p_mode, MOID_T * pproc, MOID_T * pmap, A68_PROCEDURE * z, ADDR_T pop_sp, ADDR_T pop_fp)
124 {
125 if (pmap != M_VOID && p_mode != pmap) {
126 genie_partial_call (p, p_mode, pproc, pmap, *z, pop_sp, pop_fp);
127 } else if (STATUS (z) & STANDENV_PROC_MASK) {
128 NODE_T *save = A68 (f_entry);
129 A68 (f_entry) = p;
130 (void) ((*(PROCEDURE (&(BODY (z))))) (p));
131 A68 (f_entry) = save;
132 } else if (STATUS (z) & SKIP_PROCEDURE_MASK) {
133 A68_SP = pop_sp;
134 genie_push_undefined (p, SUB ((MOID (z))));
135 } else {
136 NODE_T *body = NODE (&(BODY (z)));
137 if (IS (body, ROUTINE_TEXT)) {
138 NODE_T *entry = SUB (body);
139 ADDR_T fp0 = 0;
140 // Copy arguments from stack to frame.
141 OPEN_PROC_FRAME (entry, ENVIRON (z));
142 INIT_STATIC_FRAME (entry);
143 FRAME_DNS (A68_FP) = pop_fp;
144 for (PACK_T *args = PACK (p_mode); args != NO_PACK; FORWARD (args)) {
145 int size = SIZE (MOID (args));
146 COPY ((FRAME_OBJECT (fp0)), STACK_ADDRESS (pop_sp + fp0), size);
147 fp0 += size;
148 }
149 A68_SP = pop_sp;
150 ARGSIZE (GINFO (p)) = fp0;
151 // Interpret routine text.
152 if (DIM (p_mode) > 0) {
153 // With PARAMETERS.
154 entry = NEXT (NEXT_NEXT (entry));
155 } else {
156 // Without PARAMETERS.
157 entry = NEXT_NEXT (entry);
158 }
159 GENIE_UNIT_TRACE (entry);
160 if (A68_FP == A68_MON (finish_frame_pointer)) {
161 change_masks (TOP_NODE (&A68_JOB), BREAKPOINT_INTERRUPT_MASK, A68_TRUE);
162 }
163 CLOSE_FRAME;
164 STACK_DNS (p, SUB (p_mode), A68_FP);
165 } else {
166 OPEN_PROC_FRAME (body, ENVIRON (z));
167 INIT_STATIC_FRAME (body);
168 FRAME_DNS (A68_FP) = pop_fp;
169 GENIE_UNIT_TRACE (body);
170 if (A68_FP == A68_MON (finish_frame_pointer)) {
171 change_masks (TOP_NODE (&A68_JOB), BREAKPOINT_INTERRUPT_MASK, A68_TRUE);
172 }
173 CLOSE_FRAME;
174 STACK_DNS (p, SUB (p_mode), A68_FP);
175 }
176 }
177 }
178
179 //! @brief Call event routine.
180
181 void genie_call_event_routine (NODE_T * p, MOID_T * m, A68_PROCEDURE * proc, ADDR_T pop_sp, ADDR_T pop_fp)
182 {
183 if (NODE (&(BODY (proc))) != NO_NODE) {
184 A68_PROCEDURE save = *proc;
185 set_default_event_procedure (proc);
186 genie_call_procedure (p, MOID (&save), m, m, &save, pop_sp, pop_fp);
187 (*proc) = save;
188 }
189 }
190
191 //! @brief Call PROC with arguments and push result.
192
193 PROP_T genie_call_standenv_quick (NODE_T * p)
194 {
195 NODE_T *save = A68 (f_entry);
196 A68 (f_entry) = p;
197 NODE_T *pr = SUB (p);
198 TAG_T *proc = TAX (SOURCE (&GPROP (pr)));
199 // Get arguments.
200 for (NODE_T *q = SEQUENCE (p); q != NO_NODE; q = SEQUENCE (q)) {
201 GENIE_UNIT_NO_GC (q);
202 STACK_DNS (p, MOID (q), A68_FP);
203 }
204 (void) ((*(PROCEDURE (proc))) (p));
205 A68 (f_entry) = save;
206 return GPROP (p);
207 }
208
209 //! @brief Call PROC with arguments and push result.
210
211 PROP_T genie_call_quick (NODE_T * p)
212 {
213 ADDR_T pop_sp = A68_SP, pop_fp = A68_FP;
214 // Get procedure.
215 NODE_T *proc = SUB (p);
216 GENIE_UNIT_NO_GC (proc);
217 A68_PROCEDURE z;
218 POP_OBJECT (proc, &z, A68_PROCEDURE);
219 genie_check_initialisation (p, (BYTE_T *) & z, MOID (proc));
220 // Get arguments.
221 if (SEQUENCE (p) == NO_NODE && !STATUS_TEST (p, SEQUENCE_MASK)) {
222 NODE_T top_seq;
223 GINFO_T g;
224 NODE_T *seq = &top_seq;
225 GINFO (seq) = &g;
226 SEQUENCE (seq) = NO_NODE;
227 genie_argument (NEXT (proc), &seq);
228 SEQUENCE (p) = SEQUENCE (&top_seq);
229 STATUS_SET (p, SEQUENCE_MASK);
230 } else {
231 for (NODE_T *q = SEQUENCE (p); q != NO_NODE; q = SEQUENCE (q)) {
232 GENIE_UNIT_NO_GC (q);
233 STACK_DNS (p, MOID (q), A68_FP);
234 }
235 }
236 genie_call_procedure (p, MOID (&z), PARTIAL_PROC (GINFO (proc)), PARTIAL_LOCALE (GINFO (proc)), &z, pop_sp, pop_fp);
237 return GPROP (p);
238 }
239
240 //! @brief Call PROC with arguments and push result.
241
242 PROP_T genie_call (NODE_T * p)
243 {
244 ADDR_T pop_sp = A68_SP, pop_fp = A68_FP;
245 PROP_T self;
246 UNIT (&self) = genie_call_quick;
247 SOURCE (&self) = p;
248 // Get procedure.
249 NODE_T *proc = SUB (p);
250 GENIE_UNIT_NO_GC (proc);
251 A68_PROCEDURE z;
252 POP_OBJECT (proc, &z, A68_PROCEDURE);
253 genie_check_initialisation (p, (BYTE_T *) & z, MOID (proc));
254 // Get arguments.
255 if (SEQUENCE (p) == NO_NODE && !STATUS_TEST (p, SEQUENCE_MASK)) {
256 NODE_T top_seq;
257 GINFO_T g;
258 NODE_T *seq = &top_seq;
259 GINFO (seq) = &g;
260 SEQUENCE (seq) = NO_NODE;
261 genie_argument (NEXT (proc), &seq);
262 SEQUENCE (p) = SEQUENCE (&top_seq);
263 STATUS_SET (p, SEQUENCE_MASK);
264 } else {
265 for (NODE_T *q = SEQUENCE (p); q != NO_NODE; q = SEQUENCE (q)) {
266 GENIE_UNIT_NO_GC (q);
267 }
268 }
269 genie_call_procedure (p, MOID (&z), PARTIAL_PROC (GINFO (proc)), PARTIAL_LOCALE (GINFO (proc)), &z, pop_sp, pop_fp);
270 if (PARTIAL_LOCALE (GINFO (proc)) != M_VOID && MOID (&z) != PARTIAL_LOCALE (GINFO (proc))) {
271 ;
272 } else if (STATUS (&z) & STANDENV_PROC_MASK) {
273 if (UNIT (&GPROP (proc)) == genie_identifier_standenv_proc) {
274 UNIT (&self) = genie_call_standenv_quick;
275 }
276 }
277 return self;
278 }
© 2002-2024 J.M. van der Veer (jmvdveer@xs4all.nl)
|