summaryrefslogtreecommitdiffstats
path: root/stdlib
Commit message (Collapse)AuthorAgeFilesLines
* quips: new one about C++ protected members.Kaz Kylheku2022-01-051-0/+1
| | | | * stdlib/quips.tl (%quips%): New entry.
* Version 273.txr-273Kaz Kylheku2021-12-281-1/+1
| | | | | | | | | | | | * RELNOTES: Updated. * configure (txr_ver): Bumped version. * stdlib/ver.tl (lib-version): Bumped. * txr.1: Bumped version and date. * txr.vim, tl.vim: Regenerated.
* doc: document *struct-clause-expander*.Kaz Kylheku2021-12-271-0/+1
| | | | | | | * txr.1: New section about special variable *struct-clause-expander*. * stdlib/doc-syms.tl: Updated.
* match: allow bound variables with regex modifier.Kaz Kylheku2021-12-271-0/+11
| | | | | | | | | * stdlib/match.tl (expand-quasi-match): Add regex cases with bound variable. * tests/011/patmatch.tl: Test cases for this. * txr.1: Documented.
* match: bad compile-error call in quasi matcher.Kaz Kylheku2021-12-271-1/+1
| | | | | * stdlib/match.tl (expand-quasi-match): Fix too few arguments to compile-error for format args.
* new feature: :mass-delegate struct clause macro.Kaz Kylheku2021-12-232-0/+28
| | | | | | | | | | | | | | | With :mass-delegate, it is possible to generate delegation methods in bulk. All of the methods of a struct type can be mirrored by delegates in another struct type just by writing a single :mass-delegate clause. * stdlib/struct.tlk (:mass-delegate): New struct clause macro. * tests/012/oop.tl: New tests. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* new feature: defstruct clause macros.Kaz Kylheku2021-12-222-17/+56
| | | | | | | | | | | | | | | | | | | | | | * lisplib.c (struct_set_entries): Trigger autoload on new symbols define-struct-clause and *struct-clause-expander*. * stdlib/struct.tl (*struct-clause-expander*): New variable. (defstruct): expand-slot local function now returns list of expanded slots, not a single slot; every case in the tree-case is converted to return a list. The syntax of a slot clause is first expanded through *struct-clause-expander hash; if that works then the resulting list is further scanned for expansions. (define-struct-clause): New macro. (:delegate): New struct clause defined with define-struct-clause. Provides single-slot delegation. * tests/012/oop.tl: Tests for :delegate. * txr.1: Documented define-struct-clause and :delegate. * stdlib/doc-syms.tl: Updated.
* defstruct: refactor with local function.Kaz Kylheku2021-12-221-147/+146
| | | | | | | | * stdlib/struct.tl (defstruct): Move the large tree-case in the loop which calculates the expanded slot syntax into a local function called expand-slot. This anticipates the addition of application-defined slot expanders, which will produce output that has to be recursed upon.
* The pairlis function comes to TXR Lisp.Kaz Kylheku2021-12-221-0/+1
| | | | | | | | | | | | * eval.c (eval_init): Register pairlis intrinsic. * lib.c, lib.h (pairlis): New function. * tests/012/seq.tl: New test cases. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* New functions: subq, subql, subqual and subst.Kaz Kylheku2021-12-222-5/+4
| | | | | | | | | | | | | | | * eval.c (eval_init): Register new intrinsics. * lib.c, lib.h (subq, subql, subqual, subst): New functions. * tests/012/seq.tl: New test cases. * stdlib/optimize.tl (subst): Function removed. The new subst drop-in replaces this one. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* product/arithmetic each: missing block nil.Kaz Kylheku2021-12-202-32/+53
| | | | | | | | | | | | | | | | | | | * stdlib/arith-each.tl (sys:vars-check): New function, copy and pasted from each-prod.tl. (sys:arith-each): New macro. (sum-each, sum-each*, mul-each, mul-each*): Reworked using sys:arith-each macro. This macro uses logic borrowed from a stripped-down expand-each in the compiler. * stdlib/each-prod.tl (sys:expand-each-prod, sys:expand-arith-each-prod*): Add the block nil around the mapping call, taking care that the initialization forms are evaluated outside of the block, and their values bound to gensyms that then form the function arguments. * txr.1: Document the missing requirements for all the affected macros that there must be an anonymous block around the body, which, if used, determines the return value.
* tree: new functions for priority queue operation.Kaz Kylheku2021-12-181-0/+4
| | | | | | | | | | | | | | | | * tree.c (tree_min_node, tree_min, tree_del_min_node, tree_del_min): New functions. (tree_init): tree-min-node, tree-min, tree-del-min-node, tree-del-min: New intrinsics registered. * tree.h (tree_min_node, tree_min, tree_del_min_node, tree_del_min): Declared. * txr.1: Documented. * tests/010/tree.tl: New tests. * stdlib/doc-syms.tl: Updated.
* tree: support for duplicate keys.Kaz Kylheku2021-12-171-0/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * tree.c (tr_insert): New argument for allowing duplicate. If it is true, suppresses the case of replacing a node, causing the logic to fall through to traversing right, so the duplicate key effectively looks like it is greater than the existing duplicates, and gets inserted as the rightmost duplicate. (tr_do_delete_specific, tr_delete_specific): New static functions. (tree_insert_node): New parameter, passed to tr_insert. (tree_insert): New parameter, passed to tree_insert_node. (tree_delete_specific_node): New function. (tree): New parameter to allow duplicate keys in the elements sequence. (tree_construct): Pass t to tree to allow duplicate elements. (tree_init): Update registrations of tree, tree-insert and tree-insert-node. Register tree-delete-specific-node function. * tree.h (tree, tree_insert_node, tree_insert): Declarations updated. (tree_delete_specific_node): Declared. * lib.c (seq): Pass t argument to tree_insert, allowing duplicates. * parser.c (circ_backpatch): Likewise. * parser.y (tree): Pass t to new argument of tree, so duplicates are preserved in the element list of the #T literal. * y.tab.c.shipped: Updated. * tests/010/tree.tl: Test cases for duplicate keys. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* compiler: fix broken (compile '(lambda ...)).Kaz Kylheku2021-12-141-15/+17
| | | | | | | | | | * stdlib/compiler.tl (compile): The symbol-function function returns true for lambda and that's where we are handling lambda expressions. However, the (set (symbol-function ...) ...) then fails: that requires a function name that designates a mutable function location. Let's restructure the code with match-case, and handle the lambda pattern separately via compile-toplevel.
* compiler: small end/jend issue in late-peephole.Kaz Kylheku2021-12-111-1/+1
| | | | | | | | | * stdlib/optimize.tl (basic-blocks late-peephole): In one pattern, an instruction that is recognized as (jend ...) is inadvertently rewritten to (end ...). Since this is the last optimization stage, currently, and end and jend are synonyms for the same opcode, it doesn't matter. But it could turn into a bug; let's fix it.
* compiler: tweak in basic block debug print.Kaz Kylheku2021-12-111-1/+1
| | | | | | * stdlib/optimize (basic-block print): Print the label of the next block, rather than the block itself. This reduces the verbosity during debugging.
* compiler: register-compacting optimization.Kaz Kylheku2021-12-102-0/+63
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This work addresses the following issues, which cause compiled functions to use more stack space than necessary. Firstly, the compiler doesn't allocate registers tightly. Every closure's registers can start at t2, but this isn't done. Secondly, data flow optimizations eliminate registers, leaving gaps in the register allocation. Code ends up strange: you may see register t63 used in a function where the next highest register below that is t39: evidence that a large number of temporary variables got mapped to registers and eliminated. In this change, an optimization is introduced, active at *opt-level* 6 which compacts the t registers for every closure: every closure's registers are renumbered starting from t2. Then, each closure' generating close instruction is also updated to indicate the accurate number of registers ensuring no space is wasted on the stack when the closure is prepared for execution. * stdlib/compiler.tl (compiler optimize): At optimization level 6, insert a call to basic-blocks compact-tregs just before the instruction are pulled out and put through the late peephole pass. * stdlib/optimize.tl (basic-block): New slot, closer. This is pronounced "clozer", as in one who closes. For any basic block that is the head of a closure (the entry point ito the closure code), this slot is set to point to the previous block: the one which ends in the close instruction which creates this closure: the closer. This is important because the close instruction can use t registers for arguments, and those registers belong to the closure. Those argument registers must be included in the renaming. (basic-blocks): New slots closures and cl-hash. The former lists the closure head basic blocks; all the basic blocks which are the entry blocks of a closure. The cl-hash associates each head block with a list of all the blocks (including the head block). (basic-blocks identify-closures): New method. This scans the list of blocks, identifying the closure heads, and associating them with their closers, to establish the bb.closure list. Then for each closure head in the list, the graph is searched to find all the blocks of a closure, and these lists are put into the bb.cl-hash. (basic-block fill-treg-compacting-map): This method scans a basic block, ferreting out all of it t registers, and adds renaming entries for them into a hash that is passed in. If the block is the head of a closure, then the close instruction of the block's closer is also scanned for t registers: but only the arguments, not the destination register. (basic-block apply-treg-compacting-map): This method renames the t register of a block using the renaming map. It follows the registers in the same way as fill-treg-compacting-map, and consquently also goes into the close instruction to rename the argument registers, as necessary. When tweaking the close instruction, it also updates the instruction's number-of-tregs field with the newly calculated number, which comes from the map size. (basic-blocks compact-tregs): This method ties it together, using the above methods to identify the basic blocks belonging to closures, build register renaming maps for them, and hen apply the renaming.
* define-accessor: fix broken arg handling.Kaz Kylheku2021-12-101-1/+1
| | | | | | | | | | | I discovered this off chance by searching for occurrences of (let ,(zip ...) ...) or (let (,*(zip ...)) ...) in the code base, noticing an incorrect one. * stdlib/place.tl (sys:register-simple-accessor): Remove spurious list around ,(zip temps args). * tests/012/defset.tl: Test cases for define-accessor added.
* each-match macro family: missing anon block.Kaz Kylheku2021-12-081-2/+5
| | | | | | | | | | | | | | | | | * txr.1: Adding the missing requirement that each-match and the other macros in that family must have an implicit anonymous block around the body forms. This is a requirements bug, effectively: the programmer expects these operators to be consistent with the each operator, as part of the same family. * match.tl (each-match-expander): Implement the requirement. Since we are using mapping functions, we must use temporary variables: the evaluation of the expressions which produce the sequence argument values to the mapping functions must be outside of the anonymous block. The block must surround only the function call. * tests/011/patmatch.tl: Add small test case covering this.
* rot, nrot: new functions.Kaz Kylheku2021-12-071-0/+2
| | | | | | | | | | | | | | * eval.c (eval_init): nrot, rot intrinsics registered. * lib.c (nrot, rot): New functions. * lib.h (nrot, rot): Declared. * tests/012/seq.tl: New test cases. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* ret, aret: take one argument, as documented.Kaz Kylheku2021-12-071-4/+4
| | | | | | | | | | | | | | | | * stdlib/op.tl (ret, aret): Simplify implementation, without progn or @rest, or interpolation of multiple args. We use identity* to allow the resulting function to allow and ignore multiple arguments. * txr.1: Strangely, an an edit in commit 99131c676, on Sep 26, 2014, reverted the more accurate equivalence (ret x) <--> (op identity (progn @rest x)) back to the original documentation (ret x) <--> (op identity x) which matched an older implementation. Anyway, that's moot now; the documentation is updated to give the new equivalence via identity*.
* quips: gift shop exit theme.Kaz Kylheku2021-12-061-0/+1
| | | | * stdlib/quips.tl (%quips%): New one.
* tuples*: new function.Kaz Kylheku2021-12-041-0/+1
| | | | | | | | | | | | | | | * eval.c (eval_init): Register tuples* intrinsic. * lib.c (tuples_star_func): New static function. (tuples_star): New function. * lib.h (tuples_star): Declared. * tests/012/seq.tl: New test cases. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* compiler: new late-peephole case.Kaz Kylheku2021-11-291-0/+11
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This is related to the pattern in the previous commit. When we have a situation like this: lab1 mov tn nil lab2 ifq tn nil lab4 lab3 gcall tn ... We know that if lab1 is entered, then lab2 will necessarily fall through: the lab4 branch is not taken because tn is nil. But then, tn is clobbered immediately in lab3 by the gcall tn. In other words, the value stored into tn by lab1 is never used. Therefore, we can remove the "mov tn nil" instruction and move the l1 label. lab2 ifq tn nil lab4 lab1 lab3 gcall tn ... There are 74 hits for this pattern in stdlib. * stdlib/optimize.tl (basic-blocks late-peephole): Implement the above pattern.
* compiler: revise no-longer-matching late peephole case.Kaz Kylheku2021-11-291-14/+3
| | | | | | | | | | | * stdlib/optimize.tl (basic-blocks late-peephole): This pattern doesn't match any more because of code removed by the previous commit. If we shorten it by removing the lab1 block, then it matches. Because the pattern is shorter, the reduction being performed by the replacement is no longer needed; it has already been done. The remaining value is that threads the jump from lab3 to lab4. This missing threading is what I noticed when evaluating the effects of the previous patch; this restores it.
* compiler: replace late-peephole pattern with real approach.Kaz Kylheku2021-11-261-12/+8
| | | | | | | | | | | | | | | * stdlib/optimize.tl (basic-blocks merge-jump-thunks): For each group of candidate jump-blocks, search the entire basic block list for one more jump block which is identical to the others, except that it doesn't end in a jmp, but rather falls through to the same target that the group jumps to. That block is then included in the group, and also becomes the default leader since it is pushed to the front. (basic-blocks late-peephole): Remove the peephole pattern which tried to attack the same problem. The new approach is much more effective: when compiling stdlib, 77 instances occur in which such a block is identified and added! The peephole pattern only matched six times.
* quips: new ones about syntactic sugar.Kaz Kylheku2021-11-261-0/+2
| | | | * stdlib/quips.tl (sys:%quips%): New entries.
* compiler: another late peephole pattern.Kaz Kylheku2021-11-261-0/+12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | There are six hits for this in stdlib, two of them in optimize.tl itself. The situation is like: label1 (instruction ...) (jmp label3) label2 (instruction ...) label3 where (instruction ...) is identical in both places. label1 and label2 are functionally identical blocks, which means that the pattern can be rewritten as: label1 label2 (instruction ...) label3 When the label1 path is taken it's faster due to the elimination of the jmp, and code size is reduced by two instructions. This pattern may possibly the result of an imperfection in the design of the basic-blocks method merge-jump-thunks. The label1 and label2 blocks are functionally identical. But merge-jump-thunks looks strictly for blocks that end in a jmp instruction. It's possible that there was a jmp instruction and the end of the label2 block, which got eliminated before merge-jump-thunks, which is done late, just before late-peephole. * stdlib/optimize.tl (basic-blocks late-peephole): New rule for the above pattern.
* buffers: use unbuffered I/O in convenience functions.Kaz Kylheku2021-11-171-2/+2
| | | | | | | | * stdlib/getput.tl (file-get-buf, command-get-buf): If the number of bytes to read is specified, we use an unbuffered stream. A buffered stream can read more bytes in order to fill a buffer, which is undesirable when dealing with a device or pipe.
* Version 272.txr-272Kaz Kylheku2021-11-111-1/+1
| | | | | | | | | | | | * RELNOTES: Updated. * configure (txr_ver): Bumped version. * stdlib/ver.tl (lib-version): Bumped. * txr.1: Bumped version and date. * txr.vim, tl.vim: Regenerated.
* compiler: late-peephole match for a wasteful register move.Kaz Kylheku2021-11-101-0/+13
| | | | | | | | | | | | | | | | | | | | | | | | | | | | I've noticed a wasteful instruction pattern in the compiled code for the sys:awk-code-move-check function: 7: 2C020007 movsr t2 t7 8: 3800000E if t7 14 9: 00000007 10: 20050002 gcall t2 1 t9 d1 t8 t6 t7 11: 00090001 12: 00080401 13: 00070006 14: 10000002 end t2 Here, the t2 register can be replaced with t7 in the gcall and end instructions, and the movsr t2 t7 instruction can be eliminated. It looks like something that could somehow be targeted more generally with a clever peephole pattern assisted by data-flow information, but for now I'm sticking in a dumb late-peephole pattern which just looks for this very specific pattern. * stdlib/optimize.tl (basic-blocks late-peephole): Add new pattern for eliminating the move, as described above. There are several hits for this in the standard library in addition to the awk module: in the path-test, each-prod and getopts files.
* compiler: avoid eval of unsafe constantp in some situations.Kaz Kylheku2021-11-091-6/+12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | In situations when the compiler evaluates a constant expression in order to make some code generating decision, we don't just want to be using safe-const-eval. While that prevents the compiler from blowing up, and issues a diagnostic, it causes incorrect code to be generated: code which does not incorporate the unsafe expression. Concrete example: (if (sqrt -1) (foo) (bar)) if we simply evaluate (sqrt -1) with safe-const-eval, we get a diagnostic, and the value nil comes out. The compiler will thus constant-fold this to (bar). Though the diagnostic was emitted, executing the compiled code does not produce the exception from (sqrt -1) any more, but just calls bar. In certain cases where the compiler relies on the evaluation of a constant expression, we should bypass those cases when the expression is unsafe. In cases where the expression will be integrated into the output code, we can test with constantp. The same is true in some other mitigating circumstances. For instance if we test with constantp, and then require safe-const-eval to produce an integer, we are okay, because a throwing evaluation will not produce an integer. * stdlib/compiler.tl (safe-constantp): New function. (compiler (comp-if, comp-ift, lambda-apply-transform)): Use safe-constantp rather than constantp for determining whether an expression is suitable for compile-time evaluation.
* compiler: handle constant expressions that throw.Kaz Kylheku2021-11-081-17/+76
| | | | | | | | | | | | | | | | | | | | | | | | | | When the compiler evaluates constant expressions, it's possible that they throw, for instance (/ 1 0). We now handle it better; the compiler warns about it and is able to keep working, avoiding constant-folding the expression. * stdlib/compiler.tl (eval-cache-entry): New struct type. (%eval-cache%): New hash table variable. (compiler (comp-arith-form, comp-fun-form)): Add some missing rlcp calls to track locations for rewritten arithmetic expressions, so we usefullly diagnose a (sys:b/ ...) and such. (compiler (comp-if, comp-ift, comp-arith-form, comp-apply-call, reduce-constant, lambda-apply-transform)): Replace instances of eval of constantp expressions with safe-const-eval, and instances of the result of eval being quoted with safe-const-reduce. (orig-form, safe-const-reduce, safe-const-eval, eval-cache-emit-warnings): New functions. (compile-top-level, with-compilation-unit): Call eval-emit-cache-warnings to warn about constant expressions that threw. squash! compiler: handle constant expressions that throw.
* doc: spelling, doc-syms refresh.Kaz Kylheku2021-11-031-2/+2
| | | | | | | | * txr.1: Fix spelling errors that have crept in due to read-once and the quasiliteral fixes to matching. * stdlib/doc-syms.tl: Forgotten refresh, needed by the fix to the wrong random-float-inc name.
* compiler: rephrase length check with tree-case.Kaz Kylheku2021-11-021-7/+6
| | | | | | * stdlib/compiler.tl (compiler comp-arith-neg-form): Instead of the length check on the form, we can use a tree case to require three argument.
* compiler: bug: invalid transformation of (- x y ...).Kaz Kylheku2021-11-021-6/+2
| | | | | * stdlib/compiler.tl (compiler comp-arith-neg-form): Remove algebraically incorrect transformation.
* compiler: remove excess call to reduce-constant.Kaz Kylheku2021-11-021-11/+10
| | | | | | | * stdlib/compiler.tl (compiler comp-arith-form): There is no need here to pass the form through reduce-constant, since we are about to divide up its arguments and individualy reduce them, much like what that function does.
* compiler: catch bugfix.Kaz Kylheku2021-11-021-8/+7
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Commit c8f12ee44d226924b89cdd764b65a5f6a4030b81 tried to fix an aspect of this problem. I ran into an issue where the try code produced a D register as its output, and this was clobbered by the catch code. In fact, the catch code simply must not clobber the try fragment's output register. No matter what register that is, it is not safe. A writable T register could hold a variable. For instance, this infinitely looping code is miscompiled such that it terminates: (let ((x 42)) (while (eql x 42) (catch (progn (throw 'foo) x) (foo () 0)))) When the exception is caught by the (foo () 0) clause x is overwritten with that 0 value. The variable x is assigned to a register like t13, and since the progn form returns x as it value, it compiles to a fragment (tfrag) which indicates t13 as its output register. The catch code wrongly borrows ohis as its own output register, placing the 0 value into it. * stdlib/compiler.tl (compiler comp-catch): Get rid of the coreg local variable, replacing all its uses with oreg.
* compiler: don't lift top-level lambdas.Kaz Kylheku2021-11-011-1/+5
| | | | | | | | | | | | | | | | | | | The compiler is lifting top-level lambdas, such as those generated by defun, using the load-time mechanism. This has the undesireable effect of unnecessarily placing the lambdas into a D register. * stdlib/compiler.tl (*top-level*): New special variable. This indicates that the compiler is compiling code that is outside of any lambda. (compiler comp-lambda-impl): Bind *top-level* to nil when compiling lambda, so its interior is no longer at the top level. (compiler comp-lambda): Suppress the unnecessary lifting optimization if the lambda expression is in the top-level, outside of any other lambda, indicated by *top-level* being true. (compile-toplevel): Bind *top-level* to t.
* compiler: compile-toplevel: bind *load-time* to t.Kaz Kylheku2021-11-011-0/+1
| | | | | | | | * stdlib/compiler.tl (compile-toplevel): Recently, I removed the binding of *load-time* to t from this function. That is not quite right; we want to positively bind it to nil. A new top-level compile starts out in non-load-time. Suppose that some compile-time evaluation recurses into the compiler.
* rel-path, path-equal: native Windows fixes.Kaz Kylheku2021-11-011-19/+43
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The checks for native Windows are incorrect, plus there are some issues in the path-volume function. We cannot check for native Windows at macro-expansion time simply by calling (find #\\ path-sep-chars) because we compile on Cygwin where that is false. What we must do is check for being on Windows at macro-expansion time, and then in the "yes" branch of that decision, the code must perform the path-sep-char test at run-time. In the "no" branch, we can output smaller code that doesn't deal with Windows. * stdlib/copy-file.tl (if-windows, if-native-windows): New macro, which give a clear syntax to the above described testing. (path-split): Use if-native-windows. (path-volume): Use if-native-windows. In addition, fix some broken tests. The tests for a UNC path "//whatever" cannot just test that the first components are "", because that also matches the path "/". It has t be that the first two components are "", and there are more components. A similar issue occurs in the situation when there is a drive letter. We cannot conclude that if the component after the drive letter is "", then it's a drive absolute path, because that situation occurs in a path like "c:" which is relative. We also destructively manipulate the path to splice out the volume part and turn it into a simple relative or absolute path. This is because the path-simplify function deosn't deal with the volume prefix; its logic like eliminating .. navigations from root do not work if the prefix component is present. (rel-path): We handle a missing error case here: one path has volume prefix and the other doesn't. Also the error cases that can only occur on Windows are wrapped with if-windows to remove them at compile time.
* match: fix quasiliteral issue.Kaz Kylheku2021-10-261-0/+1
| | | | | | | | * stdlib/match.tl (compile-match): Handle the (sys:expr (sys:quasi ...)) case by recursing on the (sys:quasi ...) part, thus making them equivalent. This fixes the newly introduced broken test cases, and meets the newly documented requirements.
* pic: use ifa to remove repeated array access.Kaz Kylheku2021-10-261-2/+2
| | | | | | | * stdlib/pic.tl (insert-commas): Use ifa to bind the anaphoric variable it to [num (pred i)]. With the new ifa behavior involving read-place, this now prevents two accesses to the array.
* ifa: take advantage of read-once.Kaz Kylheku2021-10-261-1/+1
| | | | | | | | | | * stdlib/ifa.tl (ifa): When the form bound to the it anaphoric variable is a place, such that we use placelet, wrap the place in (read-once ...) so that multiple evaluations of it don't cause multiple accesses of the place. * txr.1: Documented.
* places: new accessor read-once.Kaz Kylheku2021-10-262-0/+12
| | | | | | | | | | | * lisplib.c (place_set_entries): Trigger autoload on read-once. * stdlib/place.t (read-once): New function and place. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* random: new function random-float-incl.Kaz Kylheku2021-10-251-1/+2
| | | | | | | | | | | | This function includes the 1.0 value excluded by random-float. * rand.c (random_float_incl): New static function. (rand_init): Register random_float_incl intrinsic. * txr.1: Document, and add discussion about uniformity requirements and what they mean and do not mean. * stdlib/doc-syms.tl: Updated.
* compiler: improvement in wasteful jmp elimination.Kaz Kylheku2021-10-232-5/+31
| | | | | | | | | | | | | | | * stdlib/compiler.tl (compiler optimize): After the dataflow-driven peephole optimization, call elim-dead-code again. * stdlib/optimize.tl (basic-blocks check-bypass-empty): New method. (basic-bocks elim-dead-code): After eliminating unreachable blocks from the list, we use check-bypass-empty to squeeze out any empty blocks: blocks that have no instructions in their list, other than the leading label. This helps elim-next-jmp to find more opportunities to eliminate a wasteful jump, because sometimes these jumps straddle over empty blocks. Furthermore, elim-next-jmp can generate more empty blocks itself; so we check for this situation, delete the blocks and iterate.
* compiler: also clear .next before re-linking graph.Kaz Kylheku2021-10-231-0/+1
| | | | | | | | | | | * stdlib/optimize.tl (basic-blocks elim-dead-code): When clearing the links before recalculating the graph, also clear the next field of every block, because link-graph only sets this if necessary, assuming that the value is already nil. Thus by not resetting it, we risk leaving stale values in these .next fields. The code reachability calculation relies on next fields, so if they falsely point to dead blocks, those blocks could be falsely retained.
* compiler: fix failing load-time tests.Kaz Kylheku2021-10-221-2/+1
| | | | | | | | | | | * stdlib/compiler.tl (usr:compile-toplevel): Do not bind *load-time* to t at the top level. The idea behind this binding was to treat load-time as a transparent form that does nothing if it occurs in the top-level since the top-level is already at load-time. However, this is problematic because it breaks the expectation that load-time calculations are factored out of a form and done prior to its evaluation, even if that form is top-level.
* ffi: deffi, deffi-cb: eliminate generated globals.Kaz Kylheku2021-10-221-31/+21
| | | | | | | | | | | | | | | | | | | The immediate problem is that with-dyn-lib creates a defvarl, but deffi uses load-time forms to refer to that. In compiled code, these load-time evaluations will occur before the defvarl exists. The conceptual problem is that with-dyn-lib might not be a top-level form. It can be conditionally executed, as it happens in stdlib/doc-syms.tl, which is now broken. Let's not use load-time, but straight lexical environments. * stdlib/ffi.tl (with-dyn-lib): Translate to a simple let which binds sys:ffi-lib as a lexical variable. (sys:with-dyn-lib-check): Use lexical-var-p to test what sys:ffi-lib is lexically bound as a variable. (deffi, sys:deffi-cb-expander): Instead of gloval defvarl variables, bind the needed pieces to lexical variables, placing the generated defun into that scope.