Mercurial > hg > xemacs-beta
comparison src/fns.c @ 5253:b6a398dbb403
Fewer algorithmic complexity surprises, nicer errors, mapcarX(), maplist()
src/ChangeLog addition:
2010-09-01 Aidan Kehoe <kehoea@parhasard.net>
* fns.c (list_merge, list_array_merge_into_list)
(list_array_merge_into_array):
Avoid algorithmic complexity surprises when checking for
circularity in these functions.
(Freduce): Fix some formatting, in passing.
(mapcarX): Drop the SOME_OR_EVERY argument to this function;
instead, take CALLER, a symbol reflecting the Lisp-visible
function that called mapcarX(). Use CALLER with
mapping_interaction_error() when sequences are modified
illegally. Don't cons with #'some, #'every, not even a little.
(Fmapconcat, FmapcarX, Fmapvector, Fmapcan, Fmapc, Fmap)
(Fmap_into, Fsome, Fevery): Call mapcarX() with its new
arguments.
(Fmapcan): Don't unnecessarily complicate the nconc call.
(maplist): Take CALLER, a symbol reflecting the Lisp-visible
function that called maplist(), rather than having separate
arguments to indicate mapl vs. mapcon.
Avoid algorithmic complexity surprises when checking for
circularity. In #'mapcon, check a given stretch of
result for well-formedness once, which was not previously the
case, despite what the comments said.
(Fmaplist, Fmapl, Fmapcon):
Call maplist() with its new arguments.
author | Aidan Kehoe <kehoea@parhasard.net> |
---|---|
date | Wed, 01 Sep 2010 12:51:32 +0100 |
parents | d579d76f3dcc |
children | b5611afbcc76 |
comparison
equal
deleted
inserted
replaced
5252:378a34562cbe | 5253:b6a398dbb403 |
---|---|
54 /* NOTE: This symbol is also used in lread.c */ | 54 /* NOTE: This symbol is also used in lread.c */ |
55 #define FEATUREP_SYNTAX | 55 #define FEATUREP_SYNTAX |
56 | 56 |
57 Lisp_Object Qstring_lessp, Qsort, Qmerge, Qfill; | 57 Lisp_Object Qstring_lessp, Qsort, Qmerge, Qfill; |
58 Lisp_Object Qidentity; | 58 Lisp_Object Qidentity; |
59 Lisp_Object Qvector, Qarray, Qbit_vector, QsortX, Qreduce, Q_from_end, Q_initial_value; | 59 Lisp_Object Qvector, Qarray, Qbit_vector, QsortX, Q_from_end, Q_initial_value; |
60 Lisp_Object Qmapconcat, QmapcarX, Qmapvector, Qmapcan, Qmapc, Qmap, Qmap_into; | |
61 Lisp_Object Qsome, Qevery, Qmaplist, Qmapl, Qmapcon, Qreduce; | |
60 | 62 |
61 Lisp_Object Qbase64_conversion_error; | 63 Lisp_Object Qbase64_conversion_error; |
62 | 64 |
63 Lisp_Object Vpath_separator; | 65 Lisp_Object Vpath_separator; |
64 | 66 |
2061 { | 2063 { |
2062 Lisp_Object value; | 2064 Lisp_Object value; |
2063 Lisp_Object tail; | 2065 Lisp_Object tail; |
2064 Lisp_Object tem; | 2066 Lisp_Object tem; |
2065 Lisp_Object l1, l2; | 2067 Lisp_Object l1, l2; |
2066 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; | 2068 Lisp_Object tortoises[2]; |
2069 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5; | |
2067 int looped = 0; | 2070 int looped = 0; |
2068 | 2071 |
2069 l1 = org_l1; | 2072 l1 = org_l1; |
2070 l2 = org_l2; | 2073 l2 = org_l2; |
2071 tail = Qnil; | 2074 tail = Qnil; |
2072 value = Qnil; | 2075 value = Qnil; |
2076 tortoises[0] = org_l1; | |
2077 tortoises[1] = org_l2; | |
2073 | 2078 |
2074 if (NULL == c_predicate) | 2079 if (NULL == c_predicate) |
2075 { | 2080 { |
2076 c_predicate = EQ (key_func, Qidentity) ? | 2081 c_predicate = EQ (key_func, Qidentity) ? |
2077 c_merge_predicate_nokey : c_merge_predicate_key; | 2082 c_merge_predicate_nokey : c_merge_predicate_key; |
2079 | 2084 |
2080 /* It is sufficient to protect org_l1 and org_l2. | 2085 /* It is sufficient to protect org_l1 and org_l2. |
2081 When l1 and l2 are updated, we copy the new values | 2086 When l1 and l2 are updated, we copy the new values |
2082 back into the org_ vars. */ | 2087 back into the org_ vars. */ |
2083 | 2088 |
2084 GCPRO4 (org_l1, org_l2, predicate, value); | 2089 GCPRO5 (org_l1, org_l2, predicate, value, tortoises[0]); |
2090 gcpro5.nvars = 2; | |
2085 | 2091 |
2086 while (1) | 2092 while (1) |
2087 { | 2093 { |
2088 if (NILP (l1)) | 2094 if (NILP (l1)) |
2089 { | 2095 { |
2118 value = tem; | 2124 value = tem; |
2119 else | 2125 else |
2120 Fsetcdr (tail, tem); | 2126 Fsetcdr (tail, tem); |
2121 tail = tem; | 2127 tail = tem; |
2122 | 2128 |
2123 if (++looped % CIRCULAR_LIST_SUSPICION_LENGTH) continue; | 2129 if (++looped > CIRCULAR_LIST_SUSPICION_LENGTH) |
2124 | 2130 { |
2125 /* Just check the lists aren't circular:*/ | 2131 if (looped & 1) |
2126 { | 2132 { |
2127 EXTERNAL_LIST_LOOP_1 (l1) | 2133 tortoises[0] = XCDR (tortoises[0]); |
2128 { | 2134 tortoises[1] = XCDR (tortoises[1]); |
2129 } | 2135 } |
2130 } | 2136 |
2131 { | 2137 if (EQ (org_l1, tortoises[0])) |
2132 EXTERNAL_LIST_LOOP_1 (l2) | 2138 { |
2133 { | 2139 signal_circular_list_error (org_l1); |
2134 } | 2140 } |
2135 } | 2141 |
2142 if (EQ (org_l2, tortoises[1])) | |
2143 { | |
2144 signal_circular_list_error (org_l2); | |
2145 } | |
2146 } | |
2136 } | 2147 } |
2137 } | 2148 } |
2138 | 2149 |
2139 static void | 2150 static void |
2140 array_merge (Lisp_Object *dest, Elemcount dest_len, | 2151 array_merge (Lisp_Object *dest, Elemcount dest_len, |
2228 Lisp_Object, | 2239 Lisp_Object, |
2229 Lisp_Object), | 2240 Lisp_Object), |
2230 Lisp_Object predicate, Lisp_Object key_func, | 2241 Lisp_Object predicate, Lisp_Object key_func, |
2231 Boolint reverse_order) | 2242 Boolint reverse_order) |
2232 { | 2243 { |
2233 Lisp_Object tail = Qnil, value = Qnil; | 2244 Lisp_Object tail = Qnil, value = Qnil, tortoise = list; |
2234 struct gcpro gcpro1, gcpro2, gcpro3; | 2245 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; |
2235 Elemcount array_index = 0; | 2246 Elemcount array_index = 0; |
2236 int looped = 0; | 2247 int looped = 0; |
2237 | 2248 |
2238 GCPRO3 (list, tail, value); | 2249 GCPRO4 (list, tail, value, tortoise); |
2239 | 2250 |
2240 while (1) | 2251 while (1) |
2241 { | 2252 { |
2242 if (NILP (list)) | 2253 if (NILP (list)) |
2243 { | 2254 { |
2295 tail = XCDR (tail); | 2306 tail = XCDR (tail); |
2296 } | 2307 } |
2297 ++array_index; | 2308 ++array_index; |
2298 } | 2309 } |
2299 | 2310 |
2300 if (++looped % CIRCULAR_LIST_SUSPICION_LENGTH) continue; | 2311 if (++looped > CIRCULAR_LIST_SUSPICION_LENGTH) |
2301 | 2312 { |
2302 { | 2313 if (looped & 1) |
2303 EXTERNAL_LIST_LOOP_1 (list) | 2314 { |
2304 { | 2315 tortoise = XCDR (tortoise); |
2305 } | 2316 } |
2306 } | 2317 |
2318 if (EQ (list, tortoise)) | |
2319 { | |
2320 signal_circular_list_error (list); | |
2321 } | |
2322 } | |
2307 } | 2323 } |
2308 } | 2324 } |
2309 | 2325 |
2310 static void | 2326 static void |
2311 list_list_merge_into_array (Lisp_Object *output, Elemcount output_len, | 2327 list_list_merge_into_array (Lisp_Object *output, Elemcount output_len, |
2375 { | 2391 { |
2376 if (NILP (list)) | 2392 if (NILP (list)) |
2377 { | 2393 { |
2378 if (array_len - array_index != output_len - output_index) | 2394 if (array_len - array_index != output_len - output_index) |
2379 { | 2395 { |
2380 invalid_state ("List length modified during merge", Qunbound); | 2396 mapping_interaction_error (Qmerge, list); |
2381 } | 2397 } |
2382 | 2398 |
2383 while (array_index < array_len) | 2399 while (array_index < array_len) |
2384 { | 2400 { |
2385 output [output_index++] = array [array_index++]; | 2401 output [output_index++] = array [array_index++]; |
4103 If VALS is non-NULL, NSEQUENCES is one, and SEQUENCES[0] is a cons, | 4119 If VALS is non-NULL, NSEQUENCES is one, and SEQUENCES[0] is a cons, |
4104 mapcarX will store the elements of SEQUENCES[0] in stack and GCPRO them, | 4120 mapcarX will store the elements of SEQUENCES[0] in stack and GCPRO them, |
4105 so FUNCTION cannot insert a non-cons into SEQUENCES[0] and throw off | 4121 so FUNCTION cannot insert a non-cons into SEQUENCES[0] and throw off |
4106 mapcarX. | 4122 mapcarX. |
4107 | 4123 |
4108 Otherwise, mapcarX signals a wrong-type-error if it encounters a | 4124 Otherwise, mapcarX signals an invalid state error (see |
4109 non-cons, non-array when traversing SEQUENCES. Common Lisp specifies in | 4125 mapping_interaction_error(), above) if it encounters a non-cons, |
4126 non-array when traversing SEQUENCES. Common Lisp specifies in | |
4110 MAPPING-DESTRUCTIVE-INTERACTION that it is an error when FUNCTION | 4127 MAPPING-DESTRUCTIVE-INTERACTION that it is an error when FUNCTION |
4111 destructively modifies SEQUENCES in a way that might affect the ongoing | 4128 destructively modifies SEQUENCES in a way that might affect the ongoing |
4112 traversal operation. | 4129 traversal operation. |
4113 | 4130 |
4114 If SOME_OR_EVERY is SOME_OR_EVERY_SOME, return the (possibly multiple) | 4131 CALLER is a symbol describing the Lisp-visible function that was called, |
4115 values given by FUNCTION the first time it is non-nil, and abandon the | 4132 and any errors thrown because SEQUENCES was modified will reflect it. |
4116 iterations. LISP_VALS must be a cons, and the return value will be | 4133 |
4117 stored in its car. If SOME_OR_EVERY is SOME_OR_EVERY_EVERY, store Qnil | 4134 If CALLER is Qsome, return the (possibly multiple) values given by |
4118 in the car of LISP_VALS if FUNCTION gives nil; otherwise leave it | 4135 FUNCTION the first time it is non-nil, and abandon the iterations. |
4119 alone. */ | 4136 LISP_VALS must be the result of calling STORE_VOID_IN_LISP on the address |
4120 | 4137 of a Lisp object, and the return value will be stored at that address. |
4121 #define SOME_OR_EVERY_NEITHER 0 | 4138 If CALLER is Qevery, LISP_VALS must also reflect a pointer to a Lisp |
4122 #define SOME_OR_EVERY_SOME 1 | 4139 object, and Qnil will be stored at that address if FUNCTION gives nil; |
4123 #define SOME_OR_EVERY_EVERY 2 | 4140 otherwise it will be left alone. */ |
4124 | 4141 |
4125 static void | 4142 static void |
4126 mapcarX (Elemcount call_count, Lisp_Object *vals, Lisp_Object lisp_vals, | 4143 mapcarX (Elemcount call_count, Lisp_Object *vals, Lisp_Object lisp_vals, |
4127 Lisp_Object function, int nsequences, Lisp_Object *sequences, | 4144 Lisp_Object function, int nsequences, Lisp_Object *sequences, |
4128 int some_or_every) | 4145 Lisp_Object caller) |
4129 { | 4146 { |
4130 Lisp_Object called, *args; | 4147 Lisp_Object called, *args; |
4131 struct gcpro gcpro1, gcpro2; | 4148 struct gcpro gcpro1, gcpro2; |
4132 int i, j; | 4149 int i, j; |
4133 enum lrecord_type lisp_vals_type; | 4150 |
4134 | 4151 assert ((EQ (caller, Qsome) || EQ (caller, Qevery)) ? vals == NULL : 1); |
4135 assert (LRECORDP (lisp_vals)); | |
4136 lisp_vals_type = (enum lrecord_type) XRECORD_LHEADER (lisp_vals)->type; | |
4137 | 4152 |
4138 args = alloca_array (Lisp_Object, nsequences + 1); | 4153 args = alloca_array (Lisp_Object, nsequences + 1); |
4139 args[0] = function; | 4154 args[0] = function; |
4140 for (i = 1; i <= nsequences; ++i) | 4155 for (i = 1; i <= nsequences; ++i) |
4141 { | 4156 { |
4175 vals[i] = IGNORE_MULTIPLE_VALUES (Ffuncall (nsequences + 1, args)); | 4190 vals[i] = IGNORE_MULTIPLE_VALUES (Ffuncall (nsequences + 1, args)); |
4176 } | 4191 } |
4177 } | 4192 } |
4178 else | 4193 else |
4179 { | 4194 { |
4195 enum lrecord_type lisp_vals_type; | |
4180 Binbyte *sequence_types = alloca_array (Binbyte, nsequences); | 4196 Binbyte *sequence_types = alloca_array (Binbyte, nsequences); |
4181 for (j = 0; j < nsequences; ++j) | 4197 for (j = 0; j < nsequences; ++j) |
4182 { | 4198 { |
4183 sequence_types[j] = XRECORD_LHEADER (sequences[j])->type; | 4199 sequence_types[j] = XRECORD_LHEADER (sequences[j])->type; |
4184 } | 4200 } |
4201 | |
4202 if (!EQ (caller, Qsome) && !EQ (caller, Qevery)) | |
4203 { | |
4204 assert (LRECORDP (lisp_vals)); | |
4205 lisp_vals_type | |
4206 = (enum lrecord_type) XRECORD_LHEADER (lisp_vals)->type; | |
4207 assert (NILP (lisp_vals) || lisp_vals_type != lrecord_type_symbol); | |
4208 } | |
4185 | 4209 |
4186 for (i = 0; i < call_count; ++i) | 4210 for (i = 0; i < call_count; ++i) |
4187 { | 4211 { |
4188 for (j = 0; j < nsequences; ++j) | 4212 for (j = 0; j < nsequences; ++j) |
4189 { | 4213 { |
4191 { | 4215 { |
4192 case lrecord_type_cons: | 4216 case lrecord_type_cons: |
4193 { | 4217 { |
4194 if (!CONSP (sequences[j])) | 4218 if (!CONSP (sequences[j])) |
4195 { | 4219 { |
4196 /* This means FUNCTION has probably messed | 4220 /* This means FUNCTION has messed around with a cons |
4197 around with a cons in one of the sequences, | 4221 in one of the sequences, since we checked the |
4198 since we checked the type | 4222 type (CHECK_SEQUENCE()) and the length and |
4199 (CHECK_SEQUENCE()) and the length and | |
4200 structure (with Flength()) correctly in our | 4223 structure (with Flength()) correctly in our |
4201 callers. */ | 4224 callers. */ |
4202 dead_wrong_type_argument (Qconsp, sequences[j]); | 4225 mapping_interaction_error (caller, sequences[j]); |
4203 } | 4226 } |
4204 args[j + 1] = XCAR (sequences[j]); | 4227 args[j + 1] = XCAR (sequences[j]); |
4205 sequences[j] = XCDR (sequences[j]); | 4228 sequences[j] = XCDR (sequences[j]); |
4206 break; | 4229 break; |
4207 } | 4230 } |
4230 if (vals != NULL) | 4253 if (vals != NULL) |
4231 { | 4254 { |
4232 vals[i] = IGNORE_MULTIPLE_VALUES (called); | 4255 vals[i] = IGNORE_MULTIPLE_VALUES (called); |
4233 gcpro2.nvars += 1; | 4256 gcpro2.nvars += 1; |
4234 } | 4257 } |
4235 else | 4258 else if (EQ (Qsome, caller)) |
4236 { | 4259 { |
4237 switch (lisp_vals_type) | 4260 if (!NILP (IGNORE_MULTIPLE_VALUES (called))) |
4238 { | 4261 { |
4239 case lrecord_type_symbol: | 4262 Lisp_Object *result |
4240 break; | 4263 = (Lisp_Object *) GET_VOID_FROM_LISP (lisp_vals); |
4241 case lrecord_type_cons: | 4264 *result = called; |
4242 { | 4265 UNGCPRO; |
4243 if (SOME_OR_EVERY_NEITHER == some_or_every) | 4266 return; |
4244 { | 4267 } |
4245 called = IGNORE_MULTIPLE_VALUES (called); | 4268 } |
4246 if (!CONSP (lisp_vals)) | 4269 else if (EQ (Qevery, caller)) |
4247 { | 4270 { |
4248 /* If FUNCTION has inserted a non-cons non-nil | 4271 if (NILP (IGNORE_MULTIPLE_VALUES (called))) |
4249 cdr into the list before we've processed the | 4272 { |
4250 relevant part, error. */ | 4273 Lisp_Object *result |
4251 dead_wrong_type_argument (Qconsp, lisp_vals); | 4274 = (Lisp_Object *) GET_VOID_FROM_LISP (lisp_vals); |
4252 } | 4275 *result = Qnil; |
4253 | 4276 UNGCPRO; |
4254 XSETCAR (lisp_vals, called); | 4277 return; |
4255 lisp_vals = XCDR (lisp_vals); | 4278 } |
4256 break; | 4279 } |
4257 } | 4280 else |
4258 | 4281 { |
4259 if (SOME_OR_EVERY_SOME == some_or_every) | 4282 called = IGNORE_MULTIPLE_VALUES (called); |
4260 { | 4283 switch (lisp_vals_type) |
4261 if (!NILP (IGNORE_MULTIPLE_VALUES (called))) | 4284 { |
4262 { | 4285 case lrecord_type_symbol: |
4263 XCAR (lisp_vals) = called; | 4286 /* This is #'mapc; the result of the funcall is |
4264 UNGCPRO; | 4287 discarded. */ |
4265 return; | 4288 break; |
4266 } | 4289 case lrecord_type_cons: |
4267 break; | 4290 { |
4268 } | 4291 if (!CONSP (lisp_vals)) |
4269 | 4292 { |
4270 if (SOME_OR_EVERY_EVERY == some_or_every) | 4293 /* If FUNCTION has inserted a non-cons non-nil |
4271 { | 4294 cdr into the list before we've processed the |
4272 called = IGNORE_MULTIPLE_VALUES (called); | 4295 relevant part, error. */ |
4273 if (NILP (called)) | 4296 mapping_interaction_error (caller, lisp_vals); |
4274 { | 4297 } |
4275 XCAR (lisp_vals) = Qnil; | 4298 XSETCAR (lisp_vals, called); |
4276 UNGCPRO; | 4299 lisp_vals = XCDR (lisp_vals); |
4277 return; | 4300 break; |
4278 } | 4301 } |
4279 break; | 4302 case lrecord_type_vector: |
4280 } | 4303 { |
4281 | 4304 i < XVECTOR_LENGTH (lisp_vals) ? |
4282 goto bad_some_or_every_flag; | 4305 (XVECTOR_DATA (lisp_vals)[i] = called) : |
4283 } | 4306 /* Let #'aset error. */ |
4284 case lrecord_type_vector: | 4307 Faset (lisp_vals, make_int (i), called); |
4285 { | 4308 break; |
4286 called = IGNORE_MULTIPLE_VALUES (called); | 4309 } |
4287 i < XVECTOR_LENGTH (lisp_vals) ? | 4310 case lrecord_type_string: |
4288 (XVECTOR_DATA (lisp_vals)[i] = called) : | 4311 { |
4289 /* Let #'aset error. */ | 4312 /* If this ever becomes a code hotspot, we can keep |
4290 Faset (lisp_vals, make_int (i), called); | 4313 around pointers into the data of the string, checking |
4291 break; | 4314 each time that it hasn't been relocated. */ |
4292 } | 4315 Faset (lisp_vals, make_int (i), called); |
4293 case lrecord_type_string: | 4316 break; |
4294 { | 4317 } |
4295 /* If this ever becomes a code hotspot, we can keep | 4318 case lrecord_type_bit_vector: |
4296 around pointers into the data of the string, checking | 4319 { |
4297 each time that it hasn't been relocated. */ | 4320 (BITP (called) && |
4298 called = IGNORE_MULTIPLE_VALUES (called); | 4321 i < bit_vector_length (XBIT_VECTOR (lisp_vals))) ? |
4299 Faset (lisp_vals, make_int (i), called); | 4322 set_bit_vector_bit (XBIT_VECTOR (lisp_vals), i, |
4300 break; | 4323 XINT (called)) : |
4301 } | 4324 (void) Faset (lisp_vals, make_int (i), called); |
4302 case lrecord_type_bit_vector: | 4325 break; |
4303 { | 4326 } |
4304 called = IGNORE_MULTIPLE_VALUES (called); | 4327 default: |
4305 (BITP (called) && | 4328 { |
4306 i < bit_vector_length (XBIT_VECTOR (lisp_vals))) ? | 4329 ABORT(); |
4307 set_bit_vector_bit (XBIT_VECTOR (lisp_vals), i, | 4330 break; |
4308 XINT (called)) : | 4331 } |
4309 (void) Faset (lisp_vals, make_int (i), called); | 4332 } |
4310 break; | 4333 } |
4311 } | |
4312 bad_some_or_every_flag: | |
4313 default: | |
4314 { | |
4315 ABORT(); | |
4316 break; | |
4317 } | |
4318 } | |
4319 } | |
4320 } | 4334 } |
4321 } | 4335 } |
4322 UNGCPRO; | 4336 UNGCPRO; |
4323 } | 4337 } |
4324 | 4338 |
4371 sequence = XCDR (sequence); | 4385 sequence = XCDR (sequence); |
4372 } | 4386 } |
4373 } | 4387 } |
4374 else | 4388 else |
4375 { | 4389 { |
4376 mapcarX (len, args0, Qnil, function, nargs - 2, args + 2, | 4390 mapcarX (len, args0, Qnil, function, nargs - 2, args + 2, Qmapconcat); |
4377 SOME_OR_EVERY_NEITHER); | |
4378 } | 4391 } |
4379 | 4392 |
4380 for (i = len - 1; i >= 0; i--) | 4393 for (i = len - 1; i >= 0; i--) |
4381 args0[i + i] = args0[i]; | 4394 args0[i + i] = args0[i]; |
4382 | 4395 |
4410 CHECK_SEQUENCE (args[i]); | 4423 CHECK_SEQUENCE (args[i]); |
4411 len = min (len, XINT (Flength (args[i]))); | 4424 len = min (len, XINT (Flength (args[i]))); |
4412 } | 4425 } |
4413 | 4426 |
4414 args0 = alloca_array (Lisp_Object, len); | 4427 args0 = alloca_array (Lisp_Object, len); |
4415 mapcarX (len, args0, Qnil, function, nargs - 1, args + 1, | 4428 mapcarX (len, args0, Qnil, function, nargs - 1, args + 1, QmapcarX); |
4416 SOME_OR_EVERY_NEITHER); | |
4417 | 4429 |
4418 return Flist ((int) len, args0); | 4430 return Flist ((int) len, args0); |
4419 } | 4431 } |
4420 | 4432 |
4421 DEFUN ("mapvector", Fmapvector, 2, MANY, 0, /* | 4433 DEFUN ("mapvector", Fmapvector, 2, MANY, 0, /* |
4447 result = make_vector (len, Qnil); | 4459 result = make_vector (len, Qnil); |
4448 GCPRO1 (result); | 4460 GCPRO1 (result); |
4449 /* Don't pass result as the lisp_object argument, we want mapcarX to protect | 4461 /* Don't pass result as the lisp_object argument, we want mapcarX to protect |
4450 a single list argument's elements from being garbage-collected. */ | 4462 a single list argument's elements from being garbage-collected. */ |
4451 mapcarX (len, XVECTOR_DATA (result), Qnil, function, nargs - 1, args +1, | 4463 mapcarX (len, XVECTOR_DATA (result), Qnil, function, nargs - 1, args +1, |
4452 SOME_OR_EVERY_NEITHER); | 4464 Qmapvector); |
4453 UNGCPRO; | 4465 RETURN_UNGCPRO (result); |
4454 | |
4455 return result; | |
4456 } | 4466 } |
4457 | 4467 |
4458 DEFUN ("mapcan", Fmapcan, 2, MANY, 0, /* | 4468 DEFUN ("mapcan", Fmapcan, 2, MANY, 0, /* |
4459 Call FUNCTION on each element of SEQUENCE; chain the results together. | 4469 Call FUNCTION on each element of SEQUENCE; chain the results together. |
4460 | 4470 |
4468 | 4478 |
4469 arguments: (FUNCTION SEQUENCE &rest SEQUENCES) | 4479 arguments: (FUNCTION SEQUENCE &rest SEQUENCES) |
4470 */ | 4480 */ |
4471 (int nargs, Lisp_Object *args)) | 4481 (int nargs, Lisp_Object *args)) |
4472 { | 4482 { |
4473 Lisp_Object function = args[0], nconcing; | 4483 Lisp_Object function = args[0], *result; |
4474 Elemcount len = EMACS_INT_MAX; | 4484 Elemcount result_len = EMACS_INT_MAX; |
4475 Lisp_Object *args0; | |
4476 struct gcpro gcpro1; | |
4477 int i; | 4485 int i; |
4478 | 4486 |
4479 for (i = 1; i < nargs; ++i) | 4487 for (i = 1; i < nargs; ++i) |
4480 { | 4488 { |
4481 CHECK_SEQUENCE (args[i]); | 4489 CHECK_SEQUENCE (args[i]); |
4482 len = min (len, XINT (Flength (args[i]))); | 4490 result_len = min (result_len, XINT (Flength (args[i]))); |
4483 } | 4491 } |
4484 | 4492 |
4485 args0 = alloca_array (Lisp_Object, len + 1); | 4493 result = alloca_array (Lisp_Object, result_len); |
4486 mapcarX (len, args0 + 1, Qnil, function, nargs - 1, args + 1, | 4494 mapcarX (result_len, result, Qnil, function, nargs - 1, args + 1, Qmapcan); |
4487 SOME_OR_EVERY_NEITHER); | 4495 |
4488 | 4496 /* #'nconc GCPROs its args in case of signals and error. */ |
4489 if (len < 2) | 4497 return Fnconc (result_len, result); |
4490 { | |
4491 return len ? args0[1] : Qnil; | |
4492 } | |
4493 | |
4494 /* bytecode_nconc2 can signal and return, we need to GCPRO the args, since | |
4495 mapcarX is no longer doing this for us. */ | |
4496 args0[0] = Fcons (Qnil, Qnil); | |
4497 GCPRO1 (args0[0]); | |
4498 gcpro1.nvars = len + 1; | |
4499 | |
4500 for (i = 0; i < len; ++i) | |
4501 { | |
4502 nconcing = bytecode_nconc2 (args0 + i); | |
4503 args0[i + 1] = nconcing; | |
4504 } | |
4505 | |
4506 RETURN_UNGCPRO (XCDR (nconcing)); | |
4507 } | 4498 } |
4508 | 4499 |
4509 DEFUN ("mapc", Fmapc, 2, MANY, 0, /* | 4500 DEFUN ("mapc", Fmapc, 2, MANY, 0, /* |
4510 Call FUNCTION on each element of SEQUENCE. | 4501 Call FUNCTION on each element of SEQUENCE. |
4511 | 4502 |
4537 | 4528 |
4538 /* We need to GCPRO sequence, because mapcarX will modify the | 4529 /* We need to GCPRO sequence, because mapcarX will modify the |
4539 elements of the args array handed to it, and this may involve | 4530 elements of the args array handed to it, and this may involve |
4540 elements of sequence getting garbage collected. */ | 4531 elements of sequence getting garbage collected. */ |
4541 GCPRO1 (sequence); | 4532 GCPRO1 (sequence); |
4542 mapcarX (len, NULL, Qnil, args[0], nargs - 1, args + 1, | 4533 mapcarX (len, NULL, Qnil, args[0], nargs - 1, args + 1, Qmapc); |
4543 SOME_OR_EVERY_NEITHER); | |
4544 RETURN_UNGCPRO (sequence); | 4534 RETURN_UNGCPRO (sequence); |
4545 } | 4535 } |
4546 | 4536 |
4547 DEFUN ("map", Fmap, 3, MANY, 0, /* | 4537 DEFUN ("map", Fmap, 3, MANY, 0, /* |
4548 Map FUNCTION across one or more sequences, returning a sequence. | 4538 Map FUNCTION across one or more sequences, returning a sequence. |
4578 if (!NILP (type)) | 4568 if (!NILP (type)) |
4579 { | 4569 { |
4580 args0 = alloca_array (Lisp_Object, len); | 4570 args0 = alloca_array (Lisp_Object, len); |
4581 } | 4571 } |
4582 | 4572 |
4583 mapcarX (len, args0, Qnil, function, nargs - 2, args + 2, | 4573 mapcarX (len, args0, Qnil, function, nargs - 2, args + 2, Qmap); |
4584 SOME_OR_EVERY_NEITHER); | |
4585 | 4574 |
4586 if (EQ (type, Qnil)) | 4575 if (EQ (type, Qnil)) |
4587 { | 4576 { |
4588 return result; | 4577 return result; |
4589 } | 4578 } |
4644 CHECK_SEQUENCE (args[i]); | 4633 CHECK_SEQUENCE (args[i]); |
4645 len = min (len, XINT (Flength (args[i]))); | 4634 len = min (len, XINT (Flength (args[i]))); |
4646 } | 4635 } |
4647 | 4636 |
4648 mapcarX (len, NULL, result_sequence, function, nargs - 2, args + 2, | 4637 mapcarX (len, NULL, result_sequence, function, nargs - 2, args + 2, |
4649 SOME_OR_EVERY_NEITHER); | 4638 Qmap_into); |
4650 | 4639 |
4651 return result_sequence; | 4640 return result_sequence; |
4652 } | 4641 } |
4653 | 4642 |
4654 DEFUN ("some", Fsome, 2, MANY, 0, /* | 4643 DEFUN ("some", Fsome, 2, MANY, 0, /* |
4661 | 4650 |
4662 arguments: (PREDICATE SEQUENCE &rest SEQUENCES) | 4651 arguments: (PREDICATE SEQUENCE &rest SEQUENCES) |
4663 */ | 4652 */ |
4664 (int nargs, Lisp_Object *args)) | 4653 (int nargs, Lisp_Object *args)) |
4665 { | 4654 { |
4666 Lisp_Object result_box = Fcons (Qnil, Qnil); | 4655 Lisp_Object result = Qnil, |
4667 struct gcpro gcpro1; | 4656 result_ptr = STORE_VOID_IN_LISP ((void *) &result); |
4668 Elemcount len = EMACS_INT_MAX; | 4657 Elemcount len = EMACS_INT_MAX; |
4669 int i; | 4658 int i; |
4670 | 4659 |
4671 GCPRO1 (result_box); | |
4672 | |
4673 for (i = 1; i < nargs; ++i) | 4660 for (i = 1; i < nargs; ++i) |
4674 { | 4661 { |
4675 CHECK_SEQUENCE (args[i]); | 4662 CHECK_SEQUENCE (args[i]); |
4676 len = min (len, XINT (Flength (args[i]))); | 4663 len = min (len, XINT (Flength (args[i]))); |
4677 } | 4664 } |
4678 | 4665 |
4679 mapcarX (len, NULL, result_box, args[0], nargs - 1, args +1, | 4666 mapcarX (len, NULL, result_ptr, args[0], nargs - 1, args +1, Qsome); |
4680 SOME_OR_EVERY_SOME); | 4667 |
4681 | 4668 return result; |
4682 RETURN_UNGCPRO (XCAR (result_box)); | |
4683 } | 4669 } |
4684 | 4670 |
4685 DEFUN ("every", Fevery, 2, MANY, 0, /* | 4671 DEFUN ("every", Fevery, 2, MANY, 0, /* |
4686 Return true if PREDICATE is true of every element of SEQUENCE. | 4672 Return true if PREDICATE is true of every element of SEQUENCE. |
4687 | 4673 |
4692 | 4678 |
4693 arguments: (PREDICATE SEQUENCE &rest SEQUENCES) | 4679 arguments: (PREDICATE SEQUENCE &rest SEQUENCES) |
4694 */ | 4680 */ |
4695 (int nargs, Lisp_Object *args)) | 4681 (int nargs, Lisp_Object *args)) |
4696 { | 4682 { |
4697 Lisp_Object result_box = Fcons (Qt, Qnil); | 4683 Lisp_Object result = Qt, result_ptr = STORE_VOID_IN_LISP ((void *) &result); |
4698 struct gcpro gcpro1; | |
4699 Elemcount len = EMACS_INT_MAX; | 4684 Elemcount len = EMACS_INT_MAX; |
4700 int i; | 4685 int i; |
4701 | 4686 |
4702 GCPRO1 (result_box); | |
4703 | |
4704 for (i = 1; i < nargs; ++i) | 4687 for (i = 1; i < nargs; ++i) |
4705 { | 4688 { |
4706 CHECK_SEQUENCE (args[i]); | 4689 CHECK_SEQUENCE (args[i]); |
4707 len = min (len, XINT (Flength (args[i]))); | 4690 len = min (len, XINT (Flength (args[i]))); |
4708 } | 4691 } |
4709 | 4692 |
4710 mapcarX (len, NULL, result_box, args[0], nargs - 1, args +1, | 4693 mapcarX (len, NULL, result_ptr, args[0], nargs - 1, args +1, Qevery); |
4711 SOME_OR_EVERY_EVERY); | 4694 |
4712 | 4695 return result; |
4713 RETURN_UNGCPRO (XCAR (result_box)); | |
4714 } | 4696 } |
4715 | 4697 |
4716 /* Call FUNCTION with NLISTS arguments repeatedly, each Nth argument | 4698 /* Call FUNCTION with NLISTS arguments repeatedly, each Nth argument |
4717 corresponding to the result of calling (nthcdr ITERATION-COUNT LISTS[N]), | 4699 corresponding to the result of calling (nthcdr ITERATION-COUNT LISTS[N]), |
4718 until that #'nthcdr expression gives nil for some element of LISTS. | 4700 until that #'nthcdr expression gives nil for some element of LISTS. |
4719 | 4701 |
4720 If MAPLP is zero, return LISTS[0]. Otherwise, return a list of the return | 4702 CALLER is a symbol reflecting the Lisp-visible function that was called, |
4721 values from FUNCTION; if NCONCP is non-zero, nconc them together. | 4703 and any errors thrown because SEQUENCES was modified will reflect it. |
4704 | |
4705 If CALLER is Qmapl, return LISTS[0]. Otherwise, return a list of the | |
4706 return values from FUNCTION; if caller is Qmapcan, nconc them together. | |
4722 | 4707 |
4723 In contrast to mapcarX, we don't require our callers to check LISTS for | 4708 In contrast to mapcarX, we don't require our callers to check LISTS for |
4724 well-formedness, we signal wrong-type-argument if it's not a list, or | 4709 well-formedness, we signal wrong-type-argument if it's not a list, or |
4725 circular-list if it's circular. */ | 4710 circular-list if it's circular. */ |
4726 | 4711 |
4727 static Lisp_Object | 4712 static Lisp_Object |
4728 maplist (Lisp_Object function, int nlists, Lisp_Object *lists, int maplp, | 4713 maplist (Lisp_Object function, int nlists, Lisp_Object *lists, |
4729 int nconcp) | 4714 Lisp_Object caller) |
4730 { | 4715 { |
4731 Lisp_Object result = maplp ? lists[0] : Fcons (Qnil, Qnil), funcalled; | 4716 Lisp_Object nconcing[2], accum = Qnil, *args, *tortoises, funcalled; |
4732 Lisp_Object nconcing[2], accum = result, *args; | 4717 Lisp_Object result = EQ (caller, Qmapl) ? lists[0] : Qnil; |
4733 struct gcpro gcpro1, gcpro2, gcpro3; | 4718 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; |
4734 int i, j, continuing = (nlists > 0), called_count = 0; | 4719 int i, j, continuing = (nlists > 0), called_count = 0; |
4735 | 4720 |
4736 args = alloca_array (Lisp_Object, nlists + 1); | 4721 args = alloca_array (Lisp_Object, nlists + 1); |
4737 args[0] = function; | 4722 args[0] = function; |
4738 for (i = 1; i <= nlists; ++i) | 4723 for (i = 1; i <= nlists; ++i) |
4739 { | 4724 { |
4740 args[i] = Qnil; | 4725 args[i] = Qnil; |
4741 } | 4726 } |
4742 | 4727 |
4743 if (nconcp) | 4728 tortoises = alloca_array (Lisp_Object, nlists); |
4744 { | 4729 memcpy (tortoises, lists, nlists * sizeof (Lisp_Object)); |
4745 nconcing[0] = result; | 4730 |
4731 if (EQ (caller, Qmapcon)) | |
4732 { | |
4733 nconcing[0] = Qnil; | |
4746 nconcing[1] = Qnil; | 4734 nconcing[1] = Qnil; |
4747 GCPRO3 (args[0], nconcing[0], result); | 4735 GCPRO4 (args[0], nconcing[0], tortoises[0], result); |
4748 gcpro1.nvars = 1; | 4736 gcpro1.nvars = 1; |
4749 gcpro2.nvars = 2; | 4737 gcpro2.nvars = 2; |
4738 gcpro3.nvars = nlists; | |
4750 } | 4739 } |
4751 else | 4740 else |
4752 { | 4741 { |
4753 GCPRO2 (args[0], result); | 4742 GCPRO3 (args[0], tortoises[0], result); |
4754 gcpro1.nvars = 1; | 4743 gcpro1.nvars = 1; |
4744 gcpro2.nvars = nlists; | |
4755 } | 4745 } |
4756 | 4746 |
4757 while (continuing) | 4747 while (continuing) |
4758 { | 4748 { |
4759 for (j = 0; j < nlists; ++j) | 4749 for (j = 0; j < nlists; ++j) |
4768 continuing = 0; | 4758 continuing = 0; |
4769 break; | 4759 break; |
4770 } | 4760 } |
4771 else | 4761 else |
4772 { | 4762 { |
4773 dead_wrong_type_argument (Qlistp, lists[j]); | 4763 lists[j] = wrong_type_argument (Qlistp, lists[j]); |
4774 } | 4764 } |
4775 } | 4765 } |
4776 if (!continuing) break; | 4766 if (!continuing) break; |
4777 funcalled = IGNORE_MULTIPLE_VALUES (Ffuncall (nlists + 1, args)); | 4767 funcalled = IGNORE_MULTIPLE_VALUES (Ffuncall (nlists + 1, args)); |
4778 if (!maplp) | 4768 |
4769 if (EQ (caller, Qmapl)) | |
4779 { | 4770 { |
4780 if (nconcp) | 4771 DO_NOTHING; |
4781 { | 4772 } |
4782 /* This order of calls means we check that each list is | 4773 else if (EQ (caller, Qmapcon)) |
4783 well-formed once and once only. The last result does | 4774 { |
4784 not have to be a list. */ | 4775 nconcing[1] = funcalled; |
4785 nconcing[1] = funcalled; | 4776 accum = bytecode_nconc2 (nconcing); |
4786 nconcing[0] = bytecode_nconc2 (nconcing); | 4777 if (NILP (result)) |
4787 } | 4778 { |
4788 else | 4779 result = accum; |
4789 { | 4780 } |
4790 /* Add to the end, avoiding the need to call nreverse | 4781 /* Only check a given stretch of result for well-formedness |
4791 once we're done: */ | 4782 once: */ |
4792 XSETCDR (accum, Fcons (funcalled, Qnil)); | 4783 nconcing[0] = funcalled; |
4793 accum = XCDR (accum); | 4784 } |
4794 } | 4785 else if (NILP (accum)) |
4786 { | |
4787 accum = result = Fcons (funcalled, Qnil); | |
4788 } | |
4789 else | |
4790 { | |
4791 /* Add to the end, avoiding the need to call nreverse | |
4792 once we're done: */ | |
4793 XSETCDR (accum, Fcons (funcalled, Qnil)); | |
4794 accum = XCDR (accum); | |
4795 } | 4795 } |
4796 | 4796 |
4797 if (++called_count % CIRCULAR_LIST_SUSPICION_LENGTH) continue; | 4797 if (++called_count > CIRCULAR_LIST_SUSPICION_LENGTH) |
4798 | 4798 { |
4799 for (j = 0; j < nlists; ++j) | 4799 if (called_count & 1) |
4800 { | 4800 { |
4801 EXTERNAL_LIST_LOOP_1 (lists[j]) | 4801 for (j = 0; j < nlists; ++j) |
4802 { | 4802 { |
4803 /* Just check the lists aren't circular, using the | 4803 tortoises[j] = XCDR (tortoises[j]); |
4804 EXTERNAL_LIST_LOOP_1 macro. */ | 4804 if (EQ (lists[j], tortoises[j])) |
4805 } | 4805 { |
4806 } | 4806 signal_circular_list_error (lists[j]); |
4807 } | 4807 } |
4808 | 4808 } |
4809 if (!maplp) | 4809 } |
4810 { | 4810 else |
4811 result = XCDR (result); | 4811 { |
4812 for (j = 0; j < nlists; ++j) | |
4813 { | |
4814 if (EQ (lists[j], tortoises[j])) | |
4815 { | |
4816 signal_circular_list_error (lists[j]); | |
4817 } | |
4818 } | |
4819 } | |
4820 } | |
4812 } | 4821 } |
4813 | 4822 |
4814 RETURN_UNGCPRO (result); | 4823 RETURN_UNGCPRO (result); |
4815 } | 4824 } |
4816 | 4825 |
4821 | 4830 |
4822 arguments: (FUNCTION LIST &rest LISTS) | 4831 arguments: (FUNCTION LIST &rest LISTS) |
4823 */ | 4832 */ |
4824 (int nargs, Lisp_Object *args)) | 4833 (int nargs, Lisp_Object *args)) |
4825 { | 4834 { |
4826 return maplist (args[0], nargs - 1, args + 1, 0, 0); | 4835 return maplist (args[0], nargs - 1, args + 1, Qmaplist); |
4827 } | 4836 } |
4828 | 4837 |
4829 DEFUN ("mapl", Fmapl, 2, MANY, 0, /* | 4838 DEFUN ("mapl", Fmapl, 2, MANY, 0, /* |
4830 Like `maplist', but do not accumulate values returned by the function. | 4839 Like `maplist', but do not accumulate values returned by the function. |
4831 | 4840 |
4832 arguments: (FUNCTION LIST &rest LISTS) | 4841 arguments: (FUNCTION LIST &rest LISTS) |
4833 */ | 4842 */ |
4834 (int nargs, Lisp_Object *args)) | 4843 (int nargs, Lisp_Object *args)) |
4835 { | 4844 { |
4836 return maplist (args[0], nargs - 1, args + 1, 1, 0); | 4845 return maplist (args[0], nargs - 1, args + 1, Qmapl); |
4837 } | 4846 } |
4838 | 4847 |
4839 DEFUN ("mapcon", Fmapcon, 2, MANY, 0, /* | 4848 DEFUN ("mapcon", Fmapcon, 2, MANY, 0, /* |
4840 Like `maplist', but chains together the values returned by FUNCTION. | 4849 Like `maplist', but chains together the values returned by FUNCTION. |
4841 | 4850 |
4844 | 4853 |
4845 arguments: (FUNCTION LIST &rest LISTS) | 4854 arguments: (FUNCTION LIST &rest LISTS) |
4846 */ | 4855 */ |
4847 (int nargs, Lisp_Object *args)) | 4856 (int nargs, Lisp_Object *args)) |
4848 { | 4857 { |
4849 return maplist (args[0], nargs - 1, args + 1, 0, 1); | 4858 return maplist (args[0], nargs - 1, args + 1, Qmapcon); |
4850 } | 4859 } |
4851 | 4860 |
4852 /* Extra random functions */ | 4861 /* Extra random functions */ |
4853 | 4862 |
4854 DEFUN ("reduce", Freduce, 2, MANY, 0, /* | 4863 DEFUN ("reduce", Freduce, 2, MANY, 0, /* |
5147 Boolint need_accum = 0; | 5156 Boolint need_accum = 0; |
5148 Lisp_Object *subsequence = NULL; | 5157 Lisp_Object *subsequence = NULL; |
5149 Elemcount counting = 0, len = 0; | 5158 Elemcount counting = 0, len = 0; |
5150 struct gcpro gcpro1; | 5159 struct gcpro gcpro1; |
5151 | 5160 |
5152 if (ending - starting && starting < ending && EMACS_INT_MAX == ending) | 5161 if (ending - starting && starting < ending |
5162 && EMACS_INT_MAX == ending) | |
5153 { | 5163 { |
5154 ending = XINT (Flength (sequence)); | 5164 ending = XINT (Flength (sequence)); |
5155 } | 5165 } |
5156 | 5166 |
5157 /* :from-end with a list; make an alloca copy of the relevant list | 5167 /* :from-end with a list; make an alloca copy of the relevant list |
5913 DEFSYMBOL (Qstring); | 5923 DEFSYMBOL (Qstring); |
5914 DEFSYMBOL (Qlist); | 5924 DEFSYMBOL (Qlist); |
5915 DEFSYMBOL (Qbit_vector); | 5925 DEFSYMBOL (Qbit_vector); |
5916 defsymbol (&QsortX, "sort*"); | 5926 defsymbol (&QsortX, "sort*"); |
5917 DEFSYMBOL (Qreduce); | 5927 DEFSYMBOL (Qreduce); |
5928 | |
5929 DEFSYMBOL (Qmapconcat); | |
5930 defsymbol (&QmapcarX, "mapcar*"); | |
5931 DEFSYMBOL (Qmapvector); | |
5932 DEFSYMBOL (Qmapcan); | |
5933 DEFSYMBOL (Qmapc); | |
5934 DEFSYMBOL (Qmap); | |
5935 DEFSYMBOL (Qmap_into); | |
5936 DEFSYMBOL (Qsome); | |
5937 DEFSYMBOL (Qevery); | |
5938 DEFSYMBOL (Qmaplist); | |
5939 DEFSYMBOL (Qmapl); | |
5940 DEFSYMBOL (Qmapcon); | |
5918 | 5941 |
5919 DEFKEYWORD (Q_from_end); | 5942 DEFKEYWORD (Q_from_end); |
5920 DEFKEYWORD (Q_initial_value); | 5943 DEFKEYWORD (Q_initial_value); |
5921 | 5944 |
5922 DEFSYMBOL (Qyes_or_no_p); | 5945 DEFSYMBOL (Qyes_or_no_p); |