summaryrefslogtreecommitdiffstats
Commit message (Collapse)AuthorAgeFilesLines
...
* doc: stray reference to *place-macro*.Kaz Kylheku2021-12-271-1/+1
| | | | | * txr.1: In description of *match-macro* fix stray copy-paste referring to *place-macro*.
* match: allow bound variables with regex modifier.Kaz Kylheku2021-12-273-1/+37
| | | | | | | | | * 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.
* doc: problems in qualisiteral pattern section.Kaz Kylheku2021-12-271-4/+2
| | | | | | | | * txr.1: Fix wrong word and number agreement: the sentence is about boudn variables. Fix bungled description of bound variable substitution; this should be a .meIP to head its own section, not in line meta-typesetting; plus the syntax must refer to a {P} and show the backticks.
* txr: do not ignore regex in positive match.Kaz Kylheku2021-12-273-44/+40
| | | | | | | | | | | | | | | | | * match.c (h_var): Refactor the logic here a bit. Without regard for whether the variable has a value, we dispatch the regex, fixed field and function cases. These handle the binding against the existing value. Then before all other cases, we check for the existing value and convert that to a literal text match. The effect of this is that now the regular expression is processed even if the variable has a value. * tests/010/span-var.txr: Last two test cases hardened a bit so they cannot fall through to a successful exit, if they invoke the wrong case. This is not related to this change. New test cases for regex span. * txr.1: Updated documentation and compatibility notes.
* txr: function span variable must match existing value.Kaz Kylheku2021-12-273-22/+278
| | | | | | | | | | | | | | | | | | | | * match.c (h_var_compat): New function; verbatim copy of existing h_var prior to this commit. (h_var): If a variable has an existing binding, but is a function spanning match, do not substitute it with text. Handle it with the ordinary case, in which we now use dest_bind instead of cons. (v_var): Similarly, here, we must also use dest_bind, rather than always freshly binding the variable. (match_compat_fixup): For 272 compatibility, substitute h_var_compat for h_var in the horizontal directive table. * tests/010/span-var.txr: New test cases. * txr.1: Documentation updated and also improved overall. The behavior when a variable has an existing value is clarified for the regex and fixed field case. Also update and condense compat notes for 272.
* txr: allow variable to span vertical function.Kaz Kylheku2021-12-265-7/+95
| | | | | | | | | | | | | | | * match.c (v_var_compat, v_var): New static functions. (match_files): No longer recognize v_var specially; it is now handled via vertical table. (dir_tables_init): Register a vertical sys:var directive also via v_var function. (match_compat_fixup): New function. * txr.c (compat): Call match_compat_fixup. * tests/010/span-var.txr: New file. * txr.1: Documented.
* new feature: :mass-delegate struct clause macro.Kaz Kylheku2021-12-234-0/+213
| | | | | | | | | | | | | | | 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.
* eval: fix optional parameter bug from 2014.Kaz Kylheku2021-12-232-4/+17
| | | | | | | | | | | | | | | | | | | | | | | | | | | | This bug affects optional parameters which either have no default expression, or one that is nil. For instance x in (lambda (: (x nil))) or (lambda (: x)). When such a parameter is given the : symbol as an argument, it is not being bound, as if it weren't there. ((lambda (: x) x) :) -> ;; error: unbound variable x This issue is not a regression; it was introduced in the commit which introduced the colon convention to optionals, as well as init expressions and presence-indicating variables, commit 68c084269581f32f0a7b859446ae2efb6c6a26c0 made in February 2014. This might be the first instance of an interpreter bug being found that is not present in the compiler. * eval.c (bind_args): The idea here was that when the argument to an optional the colon keyword symbol, and the optional's initform is nil, we can skip the overhead of calling eval to get that initform's value. Unfortunately, the skip was extended over the code which binds the parameter. Only the eval can be skipped! * tests/012/lambda.tl: New test cases to cover this.
* new feature: defstruct clause macros.Kaz Kylheku2021-12-225-18/+313
| | | | | | | | | | | | | | | | | | | | | | * 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-226-0/+87
| | | | | | | | | | | | * 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-227-5/+181
| | | | | | | | | | | | | | | * 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-203-63/+91
| | | | | | | | | | | | | | | | | | | * 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.
* maprodo: bugfix: spurious return value.Kaz Kylheku2021-12-201-9/+7
| | | | | | | | | | | | | | | There are cases when maprodo returns a non-nil value, even though it is supposed to collect nothing. This is because though it is is collecting nothing, that nothing is sometimes converted to an alternative return type via make_like. * eval.c (prod_common): We allow the collect_fn function pointer to be null, to indicate nothing is to be collected, rather than using a stub. If collect_fn is null, we just call the mapping function without collecting its value, and at the end, we do not involve make_like and just return nil. (collect_nothing): Static function removed. (maprodo): Pass null function pointer instead of collect_nothing.
* less: bug, vectors not supported.Kaz Kylheku2021-12-202-1/+23
| | | | | | | | * lib.c (less_tab_init): Add missing initialization for VEC, with a priority above CONS: all vectors are greater than conses. The BUF priority is bumped to 7. * test/012/less.tl: New file.
* tree: new functions for priority queue operation.Kaz Kylheku2021-12-185-0/+156
| | | | | | | | | | | | | | | | * 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: bugfix wrong tree-count.Kaz Kylheku2021-12-182-2/+14
| | | | | | | | | | | | | When duplicate keys are inserted in the default way with replacement, the tree size must not be incremented. * tree.c (tr_insert): Increment the tr->size and maintain tr->max_size here. In the case of replacing an existing node, do not touch the count. * tests/010/tree.tl: Add test cases covering duplicate insertion and tree-count. (tree_insert_node): Remove unconditional size increment.
* tree: support for duplicate keys.Kaz Kylheku2021-12-179-28/+309
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * 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.
* tree-count: new function.Kaz Kylheku2021-12-175-2/+33
| | | | | | | | | | | | | * tree.c (tree_count): New function. (tree_init): tree-count intrinsic registered. * tree.h (tree_count): Declared. * lib.c (length): Support search tree argument via tree_count. * tests/010/tree.tl: Test cases for tree-count, indirectly via len. * txr.1: Documented.
* iter-reset: gc problem.Kaz Kylheku2021-12-171-0/+3
| | | | | | | | | | | | | * lib.c (iter_reset): When we reinitialize the iterator, it can allocate a new secondary object, e.g. using hash_begin, which is stored into the iterator. This is potentially a wrong-way assignment in terms of GC generations and so we must call mut(iter) to indicate that the object has been suspiciously mutated. We only do this if the iterator has a mark function. If it doesn't have one, then it isn't wrapping a heap object, and so doesn't have this issue. (seq_reset): This has the same issue, and the fix is the same. Since ths function is obsolescent, we don't bother doing the si->ops->mark check; we optimize for code size instead.
* iter-begin: gc problem.Kaz Kylheku2021-12-171-4/+8
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Issue 1: the seq_iter_init_with_info function potentially allocates an object via hash_begin or tree_begin and installs it into the iterator. The problem is that under iter_begin, the iterator is a heaped object; this extra allocation can trigger gc which pushes the iterator into the mature generation; yet the assignment in seq_iter_init_with_info is just a plain assignment without using the set macro. Issue 2: when gc is triggered in the above situations, it crashes due to the struct seq_iter being incompletely initialized. The mark function tries to dereference the si->ops pointer. Alas, this is initialized in the wrong order inside seq_iter_init_with_info. Concretely, tree_begin is called first, and then the it->ops = &si_tree_ops assignment is performed, which means that if the garbage collector runs under tree_begin, it sees a null it->ops pointer. However, this issue cannot just be fixed here by rearranging the code because that leaves Issue 1 unsolved. Also, this initialization order is not an issue for stack-allocated struct seq_iters. The fix for Issue 1 and Issue 2 is to reorder things in iter_begin. Initialize the iterator structure first, and then create the iterator cobj. Now, of course, that goes against the usual correct protocol for object initialization. If we just do this re-ordering naively, we have Issue 3: the familiar problem that the cobj() call triggers gc, and the iterator object (e.g. from tree_iter) that has been stored into the seq_iter structure is not visible ot the GC, and is reclaimed. * lib.c (iter_begin): reorder the calls so that seq_iter_init_with_info is called first, and then the cobj to create from it the heap-allocated iterator, taking care of Issue 1 and Issue 2. To avoid Issue 3, after initializing the structure, we pull out the vulnerable iterator object into a local variable, and pass it to gc_hint(), to ensure that the variable is spilled into the stack, thereby protecting it from reclamation. (seq_begin): This function has exactly the same issue, fixed in the same way.
* 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-102-1/+13
| | | | | | | | | | | 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-083-4/+22
| | | | | | | | | | | | | | | | | * 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.
* case macros: bug in singleton key optimization.Kaz Kylheku2021-12-082-1/+33
| | | | | | | | | | | | * eval.c (me_case): Reduce (key) to key only if key is an atom. Otherwise we reduce ((a b c)), which is a single list-valued key to (a b c), which looks like three keys. This was introduced on Oct 25, 2017 in commit b72c9309c8d8f1af320dce616a69412510531b48, making it a regression. * tests/012/case.tl: New file. The last test case fails without this bugfix. The others pass either way.
* rot, nrot: new functions.Kaz Kylheku2021-12-076-0/+239
| | | | | | | | | | | | | | * 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-072-6/+6
| | | | | | | | | | | | | | | | * 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-046-0/+170
| | | | | | | | | | | | | | | * 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.
* tuples: change to abstract iteration.Kaz Kylheku2021-12-021-10/+16
| | | | | | | | | | | | | | | * lib.c (make_like): In the COBJ case, recognize an iterator object. Pull out the underlying object and recurse on it. This is needed in tuples_func, where make_like will now be called on the abstract iterator, rather than the actual sequence object. (tuples_func): The incoming object is now an iterator, and not a sequence; we need to handle it with iter_more, iter_item and iter_step. (tuples): Instead of nullify, begin iteration with iter_begin, and use iter_more to test for empty. In non-empty case, put propagate the iterator thorugh the lazy cons car field, rather than the sequence.
* tuples: add test cases.Kaz Kylheku2021-12-021-1/+48
| | | | * tests/012/seq.tl: Numerous test cases for tuples.
* tuples: check length argument.Kaz Kylheku2021-12-022-0/+7
| | | | | | | * lib.c (tuples): Check that n argument giving tuple size is a is a positive integer. * tests/012/seql.tl: Test case added.
* 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-172-2/+8
| | | | | | | | * 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.
* hash: 64 bit string and buffer hashing and seeds.Kaz Kylheku2021-11-174-60/+172
| | | | | | | | | | | | | | | | | | * hash.c (randbox, hash_c_str, hash_buf): Separate implementation for 64 bit pointers, using 64 bit random values, and producing a 64 bit hash, taking in a 64 bit seed. (gen_hash_seed): Use time_sec_nsec to get nanoseconds. On 64 bit, put together the seed differently to generate a wider value. * tests/009/json.txr: Change from hash tables to lists, so the order of the output doesn't change between 64 and 32 bits, due to the different string hashing. * tests/009/json.expected: Updated. * txr.1: Documented that seeds are up to 64 bits, but with possibly only the lower 32 bits being used.
* doc: fix misuses of << formatting code.Kaz Kylheku2021-11-161-8/+8
| | | | | * txr.1: Fix bunch of instances of formatting like << foo) which should be << foo ).
* Version 272.txr-272Kaz Kylheku2021-11-116-1105/+1183
| | | | | | | | | | | | * 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.
* hash: spurious space in printed representation.Kaz Kylheku2021-11-081-6/+10
| | | | | * hash.c (hash_print_op): Only set the need_space flag if some leading item is printed.
* doc: mention :weak-or and :weak-and in syntax.Kaz Kylheku2021-11-081-1/+1
| | | | | * txr.1: The syntax synopsis for the hash function neglects to mention the :weak-or and :weak-and symbols.