summaryrefslogtreecommitdiffstats
Commit message (Collapse)AuthorAgeFilesLines
* search/rsearch: wrong object in bad key diagnostic.Kaz Kylheku2020-05-251-2/+2
| | | | | * lib.c (search_list, rsearch_list): When the key has a bad type, don't report the seq object in its place.
* lib: combine cat_str and vscat implementations.Kaz Kylheku2020-05-241-105/+91
| | | | | | | | | | | | | | | | | | | | | | | The vscat function is white-box copy of cat_str, with just the iteration over the inputs done differently, and without the support for separators that are characters instead of strings (which was added to cat_str after vscat was forked. In this patch, the common logic underlying both functions is factored out into a small ad-hoc "struct cat_str" object which maintains the state and provides the operations to measure the pieces of the string, allocate the space, copy the pieces together and produce the resulting object. The motivation here isn't just to reduce duplication. I would like a more efficient function for catenating strings which takes a "struct args *", not requiring a list to be consed up. * lib.c (struct cat_str): New struct type. (cat_str_init, cat_str_measure, cat_str_alloc, cat_str_append, cat_str_get): New static functions. (cat_str, vscat): Considerably shorten by using the above functions.
* Fix few typos reported from Fossies.Kaz Kylheku2020-05-232-3/+3
| | | | | | | | | | | | | Fossies administrator Jens alerted me to some typos. * txr.1: Fix two instances of alphanumeric being hyphenated, and one case of invocable being rendered as invokable. * linenoise/linenoise.c (struct lino_state): Misspelled "buffer" in a comment. One other comment typos in this file is from the original code, so it stays: who needs yet another merge conflict? Not touching the original typo in example.c, either.
* Version 238.txr-238Kaz Kylheku2020-05-186-803/+839
| | | | | | | | | | * RELNOTES: Updated. * configure, txr.1: Bumped version and date. * share/txr/stdlib/ver.tl: Likewise. * txr.vim, tl.vim: Regenerated.
* New assert macro.Kaz Kylheku2020-05-184-0/+88
| | | | | | | | | | | | * eval.c (rt_assert_fail, me_assert): New static functions. (eval_init): assert macro and sys:rt-assert-fail function registered. * lib.c (func_n4ov): New function. * lib.h (func_n4ov): Declared. * txr.1: Documented.
* sort: prudently make it subject to compat valueKaz Kylheku2020-05-143-5/+27
| | | | | | | | | | | | * eval.c (eval_init): If opt_compat is 237 or less, make sort and shuffle destructive. * share/txr/stdlib/getopts.tl (opthelp): Revert previous change, restoring use of copy-list and use nsort instead of sort, so the function is not affected by the 237 compatibility being turned on. * txr.1: Add compatibility notes.
* lib: sort becomes non-destructive; nsort introduced.Kaz Kylheku2020-05-137-22/+87
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | I'm fixing a historic mistake copied from ANSI Lisp, which trips up language newcomers and sometimes even experienced users. The function innocently named sort will now return newly allocated structure. The function previously called sort will be available as nsort (non-consing/allocating sort). The shuffle function also becomes pure, and is accompanied by nshuffle. * eval (me_op): Continue to use destructive sort in this legacy code that is only triggered in very old compat mode. (eval_init): Registered nsort and nshuffle. * lib.c (nsort, nshuffle): New functions introduced, closely based on sort and shuffle. (sort, shuffle): Rewritten to avoid destructive behavior: work by copying the input and calling destructive counterparts. (sort_group): Continue to use destructive sort, which is safe; the structure is locally allocated. The sort_group function has pure semantics. (grade): Likewise. * lib.h (nsort, nshuffle): Declared. * share/txr/stdlib/getopts.tl (opthelp): Replace an instance of the (sort (copy-list ...)) pattern with just (sort ...). * tags.tl (toplevel): Continue to use destructive sort to sort tags before writing the tag file; the lifetime of the tags list ends when the file is written. * tests/010/seq.txr: Switch some sort calls to nsort to keep test case working. * txr.1: Documented.
* lib: use seq-info for sort and shuffle.Kaz Kylheku2020-05-132-19/+45
| | | | | | | | * lib.c (sort, shuffle): Switch to seq_info. For consistency with sort, shuffle now handles hashes in the same peculiar way. * txr.1: Document hash behavior for sort and shuffle.
* Expose isatty function.Kaz Kylheku2020-05-112-0/+57
| | | | | | | * sysif.c (isatty_wrap): New function. (sysif_init): Register isatty intrinsic. * txr.1: Documented.
* doc: quote, umethod, hash-invert: bad syntaxKaz Kylheku2020-05-091-3/+3
| | | | | * txr.1: Remove manually added angle brackets on <form>. Fix inappropriate use of << and >>.
* configure: --help doesn't clober ./reconfigureKaz Kylheku2020-05-081-15/+16
| | | | | * configure: move the section of the script which produces ./reconfigure after the help processing.
* gc: fight spurious retention.Kaz Kylheku2020-05-061-4/+4
| | | | | | | | | | | | | | | | | I've noticed that the January 9, 2020 commit "gc: obtain stack top using alloca" triggers spurious retention when compiling with HAVE_VALGRIND. The finalization test case tests/012/fini.tl breaks because the expected finalizers are not called. Changing the (sys:gc) call to two calls to (sys:gc 1) makes it pass. The culprit seems to be the inlining of the complex function sweep into gc. It has local variables for which spaces has to be reserved, which are not used until after mark() is called; likely the values in those spaces are picked up by the stack scan. Let's make sure that functions called out of gc() are not inlined. * gc.c (mark, sweep, prepare_finals, call_finals): Mark NOTINLINE.
* funcall: fight spurious retention.Kaz Kylheku2020-05-051-4/+4
| | | | | * lib.c (funcall1, funcall2, funcall3, funcall4): Add forgotten argument zaps in the case that routes to generic_funcall.
* lib: convert counting and predicate quantifying to seq_info.Kaz Kylheku2020-05-051-65/+88
| | | | | | * lib.c (countqual, countql, countq, count_if, some_satisfy, all_satisfy, none_satisfy): Convert from list iteration to seq_info.
* compiler: rearrange handling of callsKaz Kylheku2020-05-041-33/+37
| | | | | | | | | | | | | | | * share/txr/stdlib/compiler.tl (compiler compile): Open up the main caseq statement for handling symbols other than just special operators. Now we handle the compiler-only special operator sys:ift here, as well as the special casing for call and apply. Function calls are handled as the fallback case here now. (compiler call-fun-form): Remove the checking for ift, and for call, apply and usr:apply. Only regular case function calls are handled here now. (compiler comp-apply-call): New method dedicated for compiling calls to the call, apply or usr:apply functions, dispatched directly out of compiler compile.
* compile: don't expand.Kaz Kylheku2020-05-031-2/+2
| | | | | | | | The compile function doesn't need to expand because the input is a function that has already been expanded. * share/txr/stdlib/compiler.tl (compile): Pass the second argument to compile-toplevel to suppress expansion.
* symbol-function: bugfix: expand lambda expression.Kaz Kylheku2020-05-032-2/+3
| | | | | | | * eval.c (lookup_fun): A lambda expression must be expanded before being turned into a function. * txr.1: Documented.
* compiler: implement lambda lifting.Kaz Kylheku2020-05-031-1/+15
| | | | | | | | | | | | | | | | | This is what the recent load-time changes were grooming the compiler toward. When we compile a lambda, we can look at the function and variable refernces it is making. If the lambda makes no lexical function or variable references, we can lift that lambda into load time, so that it's instantiated once and then re-used out of a D register. Effectively, it becomes a top-level function. * share/txr/stdlib/compiler.tl (compiler comp-lambda-impl): New method, formed by renaming comp-lambda. (compiler comp-lambda): Turned not wrapper for comp-lambda impl which compiles the lambda, and checks for the conditions for hoisting it into load time, which is currently done by generating the sys:load-time-lit form around it and re-compiling.
* compiler: honor load-time in parts of loop.Kaz Kylheku2020-05-032-0/+15
| | | | | | | | | | | | | | | | * share/txr/stdlib/compiler.tl (compiler comp-for): If a for loop occurs in the top level, or inside a load-time, then we don't want to suppress the semantics of load-time for any parts of the loop that are repeatedly evaluated. The programmer may be doing that specifically to hoist those calculations out of the loop. We thus bind *load-time* to nil after compiling the initializing and test expressions. * txr.1: New paragraph in Notes for load-time, mentioning compiler treatment of loops and lambda. The language is deliberately general rather than being specifically about the for loop, because several loop constructs compile to the for loop, and that is also subject to future changes.
* compiler: top-level is in load-time.Kaz Kylheku2020-05-031-1/+2
| | | | | | * share/txr/stdlib/compiler.tl (compile-toplevel): Bind *load-time* to t, because of course initially we are in the top level, where load-time can be eliminated.
* compiler: check constantp in load-time.Kaz Kylheku2020-05-021-1/+1
| | | | | | | | * share/txr/stdlib/compiler.tl (compiler comp-load-time-lit): Don't hoist constant expressions into load-time, since they already get hoisted into a D register. Otherwise we just end up generating load-time code that moves from one D register to another.
* compiler: treat nested load-time forms.Kaz Kylheku2020-05-022-14/+65
| | | | | | | | | | | | | | | | | | | | | | | load-time forms nested in load-time forms have no special semantics; it's wasteful to separately hoist them into load time and store their value in their own D register. We must be careful: this is not true if a nested form occurs in a lambda. * share/txr/stdlib/compiler.tl (*load-time*): New special variable. (compiler comp-lambda): Bind *load-time* to nil around the compilation of the lambda, so load-time forms in the lambda are hoisted to load time, even if the lambda itself is wrapped in a load-time form. (compiler comp-load-time-lit): Bind *load-time* true around the compilation of the form. If *load-time* is already true, then skip the special load-time logic and just compile the enclosed form; the surrounding load-time compilation is taking care of the load-time hoisting. * txr.1: Document that load-time forms nested in load-time forms don't do anything, except in the lambda case.
* doc: new typos under User-Defined Streams.Kaz Kylheku2020-05-021-4/+4
| | | | | * txr.1: Fix typos and incorrect symbol under put-buf and fill-buf method.
* compiler: load-time: eliminate temp register.Kaz Kylheku2020-05-011-5/+3
| | | | | | | | | * share/txr/stdlib/compiler.tl (compiler comp-load-time-lit): When compiling the load-time argument expression, indicate the allocated D register as the destination, rather than using a freshly allocated T register. Now we need an instruction to move into the D reg only if the fragment chose a different register.
* sockets: ipv6 address condensing rewrite.Kaz Kylheku2020-05-011-14/+12
| | | | | | | | | | | | | | | * share/txr/stdlib/socket.tl (sys:in6addr-condensed-text): Rewrite with regex based implementation that formats the number without condensing. This one has better semantics in that it finds the longest run of 0.0..0 to replace, rather than the leftmost. Ignoring this semantic difference, it also has better average performance on pseudo-random addresses, with similar performance on addresses with long condensable 0's. The original algorithm has a significantly poorer average case on random addresses, but better best case on condensable zeros like 1::1. The new algorithm could improve further with future work to make regexes faster.
* sockets: bug in formatting ipv6 address.Kaz Kylheku2020-05-011-11/+14
| | | | | | | | The str-in6addr and str-in6addr-net functions mishandle the zero address, rendering it as ":" instead of "::". * share/txr/stdlib/socket.tl (sys:in6addr-condensed-text): Test for the degenerate case and map it to "::" output.
* doc: mention zero-width bit-field at end of structure.Kaz Kylheku2020-04-271-1/+2
| | | | | * txr.1: A zero-width bit-field placed as the last member can have an effect on the structure; let's mention it.
* doc: fix wrong claim in Bitfield Allocation Rules.Kaz Kylheku2020-04-271-9/+6
| | | | | | | | * txr.1: Rewriting the last two sentences to fix the wrong claim of its last two sentences, which contradicts the correct statement in the the previous paragraph. The statements are only correct about big-endian, not about both types of machine.
* ffi: big-endian: unused parameter warnings.Kaz Kylheku2020-04-281-1/+55
| | | | | | | | | | | | | * ffi.c (ffi_i8_rput, ffi_i8_rget, ffi_u8_rput, ffi_u8_rget, ffi_i16_rput, ffi_i16_rget, ffi_u16_rput, ffi_u16_rget, ffi_i32_rput, ffi_i32_rget, ffi_u32_rput, ffi_u32_rget, ffi_char_rput, ffi_char_rget, ffi_uchar_rput, ffi_uchar_rget, ffi_bchar_rget, ffi_short_rput, ffi_short_rget, ffi_ushort_rput, ffi_ushort_rget, ffi_int_rput, ffi_int_rget, ffi_uint_rput, ffi_uint_rget, ffi_long_rput, ffi_long_rget, ffi_ulong_rput, ffi_ulong_rget, ffi_wchar_rput, ffi_wchar_rget, ffi_be_i16_rput): Add casts to suppress warnings about unused tft and self.
* Version 237.txr-237Kaz Kylheku2020-04-266-715/+771
| | | | | | | | | | * RELNOTES: Updated. * configure, txr.1: Bumped version and date. * share/txr/stdlib/ver.tl: Likewise. * txr.vim, tl.vim: Regenerated.
* Reduce footprint of :fd property.Kaz Kylheku2020-04-253-14/+8
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Querying the :fd stream property is equivalent to calling the stream-fd function. Streams have a C virtual function get_fd, so implementing :fd in their getprop functions is redundant functionality. The stream-getprop function can test for :fd and call stream-fd, so the stream implementations don't have to deal with the :fd property. Also, there are still places in the code base that are using stream_getprop to get the file descriptor, instead of calling stream_fd. If we fix all this, then fd_k remains referenced only in a very small number of places. * socket.c (dgram_get_prop): Don't handle :fd any more. * stream.c (unimpl_get_fd): Static function removed. (fill_stream_ops): Default the get_fd function to null_get_fd instead of unimpl_get_fd, so it doesn't throw. Even a stdio stream don't throw; when the file is closed, it returns nil. (stdio_get_prop): Don't handle :fd any more. (stream_get_prop): Handle :fd here. If the stream has a get_fd function that isn't null_get_fd, then call it. Only if the stream doesn't have a get_fd function, fall back on its get_prop function. * sysif.c (mkdir_wrap, poll_wrap, simulate_setuid_setgid): Call stream_fd instead of stream_get_prop.
* lib/buf: use unsigned integers around allocations.Kaz Kylheku2020-04-252-4/+4
| | | | | | | | | | * buf.c (buf_shrink): Convert len to alloc size using c_unum, which will reject negative values that will implicitly convert to a wrong/huge size. * lib.c (upcase_str, downcase_str): Similar reasoning. (sub_vec): nelem is a size_t, so use unum on it, rather than num.
* carray: revise carray diagnostics.Kaz Kylheku2020-04-241-8/+8
| | | | | | | * ffi.c (carray_dup, carray_blank, carray_ref, carray_refset, carray_replace, carray_pun): Refer to "carray" rather than "array" in diagnostic messages, which is more accurate and consistent with newer diagnostics added in previous patch.
* carray: bug sweep.Kaz Kylheku2020-04-241-28/+62
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The carray type is hereby reviewed for and repaired of issues related to the nelem member of struct carray using negative values to indicate "unknown carray size". * ffi.c (struct carray): The artype becomes a two-element array of values, so a carray can have a cache of separate null-terminated and non-null-terminated array type descriptors. This way, we get rid of the dirty trick of mutating one type. (carray_mark_op): Mark the two-element artype. (make_carray): Initialize two-element artype to nil. (carray_set_length): Cache invalidation: reset both elements of artype to nil, because the type includes length information, which may now be wrong. (copy_carray): Throw exception if asked to copy carray of unknown length. This would happen in carray_dup anyway, but at least the diagnostic refers to copy-carray. (buf_carray): Avoid calling make_duplicate_buf with negative bytes value when the carray has unknown length; diagnose. (carray_ref, carray_refset): Don't translate negative index values if the length is unknown. (carray_sub, carray_replace): If the operation requires use of the length, and it is unknown, throw an exception. (carray_ensure_artype): Take a null_term paramter that is 0 or 1, and use it as an array index into artype to set up the correct type, with the correct value of the null_term member that is henceforth not mutated. (carray_get_common, carray_put_common): Pass the null_term argument down to carray_ensure_artype. Remove the mutation of the null_term member of the type structure.
* poll: allow execution of async signal handlers.Kaz Kylheku2020-04-231-8/+7
| | | | | | | * sysif.c (poll_wrap): Allocate poll array using alloca so it will be disposed of naturally if an exception occurs. Place sig_save_enable and sig_restore_disable around poll call to allow non-deferred handler execution.
* sockets: connect bugfix and use poll in preference to select.Kaz Kylheku2020-04-232-37/+73
| | | | | | | | | | | | | | | * configure: cosmetics: change HAVE_SYS_SELECT to HAVE_SELECT, and log that the function rather than the header is being detected. * socket.c (fd_timeout): New function, implemented using poll or select. (to_connect): select logic replaced with fd_timeout. Bug fixed: connect requires polling for writability, not readability. If poll and select are not available, ignore timeout argument. (sock_accept): Use fd_timeout; fall back on non-timed-out accept if poll or select not available.
* ifa: fix broken/invalid test case.Kaz Kylheku2020-04-232-4/+7
| | | | | | | | | | | | | | | | | | * tests/012/ifa.tl: The "ambiguous" test case is not ambiguous at all. The reason it was yielding :error previously was not due to the ifa macro identifying an ambiguity but due to the funcion < not accepting nil arguments. Since < now does accept nil arguments, this test broke. Fixing this test, and adding one that tests for the ambiguous case: multiple it-candidates being rejected by ifa at expansion time. * tests/common.tl (vtest): This macro requires maintenance. To test for expansion-time failure, we must use expand, not just macroexpand. In this case, the (ifa ...) macro call is wrapped in a (let ...) so macroexpand won't do it. Secondly, the expected value is an quote expression that must be evaluated if we need its value in vtest itself. Otherwise it won't compare equal to :error, since it is actually (quote :error).
* Remove unnecessary #include directives.Kaz Kylheku2020-04-2225-54/+0
| | | | | | | | | | Time for some spring cleaning. * args.c, arith.c, buf.c, cadr.c, chksum.c, debug.c, ftw.c, gc.c, gencadr.txr, glob.c, hash.c, lisplib.c, match.c, parser.c, parser.l, parser.y, rand.c, signal.c, stream.c, strudel.c, syslog.c, tree.c, unwind.c, utf8.c, vm.c: Numerous unnecessary #include directives removed.
* mpi: remove dubious MP_DEBUG cruft.Kaz Kylheku2020-04-223-17/+0
| | | | | | | | | | | | | The MP_DEBUG macro controls the visibility of definitions that are not used anywhere. * mpi/mpi-config.h (MP_DEBUG): Removed. * mpi/mpi.c (DIAG): Macro and surrounding section controlled by #if MP_DEBUG removed. * mpi/mpi.h (MP_IOFUNC): Definition controlled by MP_DEBUG removed.
* arith: move c_unum and unum declaration into lib.hKaz Kylheku2020-04-228-8/+2
| | | | | | | | | | | | | | | | The c_unum and unum functions are fairly often needed and and are closely related to c_num and num, which have always been declared in lib.h even though they live in arith.c. Files not doing arithmetic are including "arith.h" just for the sake of c_num or unum. * arith.h (c_unum, unum): Declarations removed. * lib.h (c_unum, unum): Declarations moved here. * chksum.c, debug.c, stream.c, strudel.c, sysif.c, vm.c: These files no longer require #include "arith.h" as a result, which is therefore removed.
* printer: add package prefix on symbols with zero-length name.Kaz Kylheku2020-04-221-0/+3
| | | | | | * lib.c (symbol_needs_prefix): If the name is an empty string, the symbol needs a prefix. Otherwise it disappears in the output.
* tags: bugfix: file-opening logic in simulated load.Kaz Kylheku2020-04-221-1/+2
| | | | | | | | * tags.tl (fake-load): When the file doesn't end in .tl, we are only trying it with the .tl suffix, without falling back on the original name. And we are doing that in an ignerr, so the nil object will be used as a stream. This cause stdin to be read when the function encounters a dangling symlink.
* parser: C++ cast fix.Kaz Kylheku2020-04-221-1/+1
| | | | | * parser.l (YY_INPUT): Must use coerce because we are changing from char * to unsigned char *.
* parser: batched read operation for speed.Kaz Kylheku2020-04-223-7/+14
| | | | | | | | | | | | | | | | As a result of this change, the startup time is reduced. The command txr -e '(compile-toplevel nil)' shows a 54% speedup: around 110 milliseconds down from around 170. Programs that read large amounts of TXR Lisp data will benefit. * parser.l (YY_INPUT): Use new get_bytes function instead of get_byte to read a buffer at a time. * stream.c (get_bytes): New function. * stream.h (get_bytes): Declared.
* streams: put_buf and fill_buf become lower-level.Kaz Kylheku2020-04-224-46/+78
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | In this commit, the put_buf and fill_buf stream virtual functions are changed to operate directly on a low-level buffer, rather than a stream. This will allow these functions to be used for improving the performance of I/O operations that are not related to buffer objects. * stream.h (struct strm_ops): Change type signature of put_buf and fill_buf members. The lengths and iszes are ucnum. The return value is ucnum. The buffer is passed as a pointer and length, rather than a buffer object. * stream.c (unimpl_put_buf, unimpl_fill_buf, generic_put_buf, generic_fill_buf, stdio_put_buf, stdio_fill_buf, delegate_put_buf, delegate_fill_buf): Adjust to new interface. (put_buf, fill_buf, fill_buf_adjust): Pull the poitner and size from the buffer and pass those down to the virtual functions rather than the buffer itself. Convert ucnum return value to val. * strudel.c (strudel_put_buf, strudel_get_buf): The struct delegate interface doesn't change. The put-buf and fill-buf methods still operate on buffer objects. To glue that with the new low-level interface, we use the init_borrowed_buf trick that is was first used in ffi.c: temporary buf objects are efficiently allocated on the stack, pointing to the same memory that is coming down from the stream operation. * txr.1: Document the new restrictions on the buf argument of the put-buf and fill-buf stream delegate methods. Since the buf not a heap object, it cannot be used after the method returns.
* Reduce consing in /= function.Kaz Kylheku2020-04-211-10/+25
| | | | | | | | * arith.c (numneqv): Do not unconditionally cons all the arguments into a list. If the arguments structure has no trailing list, then just loop over the arguments in the arg array. Only cons all the arguments into a list if there is a trailing list.
* Extending =, <, >, <= and >= to work on sequences.Kaz Kylheku2020-04-212-16/+207
| | | | | | | | | | | | | * arith.c (seq_nueq, seq_lt, seq_le): New static functions. (gt, lt): Handle sequences via seq_lt. (ge, le): Handle sequences via seq_le. (numeq): Handle sequences via seq_eq. * txr.1: Documented, and also added missing documentation about comparison of ranges by these functions, fixed mistake in the syntax (> listed twice) and added some notes about symmetry of > >= and < <=.
* seq_info: bugfix: uninitialized type field.Kaz Kylheku2020-04-201-1/+1
| | | | | | | | | | | * lib.c (seq_info): Ensure the type field in the returned structure is inintialized. We are neglecting this when the type is COBJ, initializing only the kind field. This makes the in function behave unreliably over arguments that are hash tables, or vector-like sequences (objects with a length function or the carray type). Several other functions will behave unreliably for vector-like sequences: reverse, find, rfind, pos, rpos and tprint.
* compiler: avoid wasteful substitution via %bin-op%.Kaz Kylheku2020-04-201-1/+1
| | | | | | | | * share/txr/stdlib/compiler.tl (%bin-op%): Specify default value argument to relate so that arguments not found in %nary-ops% will match to nil. Otherwise the code in comp-fun-form rewrites all two-argument function calls through this relation, resulting in a bit of wasted consing.
* compiler: bugfix: constant test in 2 or 3 arg if.Kaz Kylheku2020-04-201-2/+2
| | | | | | | | | | | | | | | | * share/txr/stdlib/compiler.tl (compiler comp-if): The two and three argument cases assume that if the test is a constant expression, the consequent "then" should be unconditionally taken. The correct behavior is to evaluate the constant, which could yield nil. I checked which library code changes after this fix, and found that a number of (defset ...) forms are generating different, shorter code. This is due to (if ',restpar (if (consp ,restpar) ...)) in defset-expander. The intent there was to eliminate the inner if code entirely if respar is nil (there is no rest parameter); due to this bug, the code elimination didn't happen. The behavior is nevertheless correct because the code does nothing if restpar is nil.