commit cc58d4de56e362f5e017d0607986b2962ee47fc1 (HEAD, refs/remotes/origin/master) Author: Michael Heerdegen Date: Thu Nov 2 18:45:34 2017 +0100 Add macros `thunk-let' and `thunk-let*' * lisp/emacs-lisp/thunk.el (thunk-let, thunk-let*): New macros. * test/lisp/emacs-lisp/thunk-tests.el: (thunk-let-basic-test, thunk-let*-basic-test) (thunk-let-bound-vars-cant-be-set-test) (thunk-let-laziness-test, thunk-let*-laziness-test) (thunk-let-bad-binding-test): New tests for `thunk-let' and `thunk-let*. * doc/lispref/eval.texi (Deferred Eval): New section. * doc/lispref/elisp.texi: Update menu. diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi index c752594584..a271749e04 100644 --- a/doc/lispref/elisp.texi +++ b/doc/lispref/elisp.texi @@ -455,6 +455,7 @@ Evaluation the program). * Backquote:: Easier construction of list structure. * Eval:: How to invoke the Lisp interpreter explicitly. +* Deferred Eval:: Deferred and lazy evaluation of forms. Kinds of Forms diff --git a/doc/lispref/eval.texi b/doc/lispref/eval.texi index 064fca22ff..74fefdb71b 100644 --- a/doc/lispref/eval.texi +++ b/doc/lispref/eval.texi @@ -20,11 +20,12 @@ function @code{eval}. @ifnottex @menu -* Intro Eval:: Evaluation in the scheme of things. -* Forms:: How various sorts of objects are evaluated. -* Quoting:: Avoiding evaluation (to put constants in the program). -* Backquote:: Easier construction of list structure. -* Eval:: How to invoke the Lisp interpreter explicitly. +* Intro Eval:: Evaluation in the scheme of things. +* Forms:: How various sorts of objects are evaluated. +* Quoting:: Avoiding evaluation (to put constants in the program). +* Backquote:: Easier construction of list structure. +* Eval:: How to invoke the Lisp interpreter explicitly. +* Deferred Eval:: Deferred and lazy evaluation of forms. @end menu @node Intro Eval @@ -877,3 +878,115 @@ particular elements, like this: @end group @end example @end defvar + +@node Deferred Eval +@section Deferred and Lazy Evaluation + +@cindex deferred evaluation +@cindex lazy evaluation + + + Sometimes it is useful to delay the evaluation of an expression, for +example if you want to avoid to perform a time-consuming calculation +in the case that it turns out that the result is not needed in the +future of the program. Therefore, the @file{thunk} library provides +the following functions and macros: + +@cindex thunk +@defmac thunk-delay forms@dots{} +Return a @dfn{thunk} for evaluating the @var{forms}. A thunk is a +closure (@pxref{Closures}) that inherits the lexical enviroment of the +@code{thunk-delay} call. Using this macro requires +@code{lexical-binding}. +@end defmac + +@defun thunk-force thunk +Force @var{thunk} to perform the evaluation of the forms specified in +the @code{thunk-delay} that created the thunk. The result of the +evaluation of the last form is returned. The @var{thunk} also +``remembers'' that it has been forced: Any further calls of +@code{thunk-force} with the same @var{thunk} will just return the same +result without evaluating the forms again. +@end defun + +@defmac thunk-let (bindings@dots{}) forms@dots{} +This macro is analogous to @code{let} but creates ``lazy'' variable +bindings. Any binding has the form @w{@code{(@var{symbol} +@var{value-form})}}. Unlike @code{let}, the evaluation of any +@var{value-form} is deferred until the binding of the according +@var{symbol} is used for the first time when evaluating the +@var{forms}. Any @var{value-form} is evaluated at most once. Using +this macro requires @code{lexical-binding}. +@end defmac + +Example: + +@example +@group +(defun f (number) + (thunk-let ((derived-number + (progn (message "Calculating 1 plus 2 times %d" number) + (1+ (* 2 number))))) + (if (> number 10) + derived-number + number))) +@end group + +@group +(f 5) +@result{} 5 +@end group + +@group +(f 12) +@print{} Calculating 1 plus 2 times 12 +@result{} 25 +@end group + +@end example + +Because of the special nature of lazily bound variables, it is an error +to set them (e.g.@: with @code{setq}). + + +@defmac thunk-let* (bindings@dots{}) forms@dots{} +This is like @code{thunk-let} but any expression in @var{bindings} is allowed +to refer to preceding bindings in this @code{thunk-let*} form. Using +this macro requires @code{lexical-binding}. +@end defmac + +@example +@group +(thunk-let* ((x (prog2 (message "Calculating x...") + (+ 1 1) + (message "Finished calculating x"))) + (y (prog2 (message "Calculating y...") + (+ x 1) + (message "Finished calculating y"))) + (z (prog2 (message "Calculating z...") + (+ y 1) + (message "Finished calculating z"))) + (a (prog2 (message "Calculating a...") + (+ z 1) + (message "Finished calculating a")))) + (* z x)) + +@print{} Calculating z... +@print{} Calculating y... +@print{} Calculating x... +@print{} Finished calculating x +@print{} Finished calculating y +@print{} Finished calculating z +@result{} 8 + +@end group +@end example + +@code{thunk-let} and @code{thunk-let*} use thunks implicitly: their +expansion creates helper symbols and binds them to thunks wrapping the +binding expressions. All references to the original variables in the +body @var{forms} are then replaced by an expression that calls +@code{thunk-force} with the according helper variable as the argument. +So, any code using @code{thunk-let} or @code{thunk-let*} could be +rewritten to use thunks, but in many cases using these macros results +in nicer code than using thunks explicitly. diff --git a/etc/NEWS b/etc/NEWS index c47ca42d27..6b3e7fc244 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -109,6 +109,10 @@ Snake and Pong are more playable on HiDPI displays. *** Completing filenames in the minibuffer via 'C-TAB' now uses the styles as configured by the variable 'completion-styles'. +** New macros 'thunk-let' and 'thunk-let*'. +These macros are analogue to 'let' and 'let*', but create bindings that +are evaluated lazily. + * New Modes and Packages in Emacs 27.1 diff --git a/lisp/emacs-lisp/thunk.el b/lisp/emacs-lisp/thunk.el index 371d10444b..895fa86722 100644 --- a/lisp/emacs-lisp/thunk.el +++ b/lisp/emacs-lisp/thunk.el @@ -41,6 +41,10 @@ ;; following: ;; ;; (thunk-force delayed) +;; +;; This file also defines macros `thunk-let' and `thunk-let*' that are +;; analogous to `let' and `let*' but provide lazy evaluation of +;; bindings by using thunks implicitly (i.e. in the expansion). ;;; Code: @@ -71,5 +75,60 @@ with the same DELAYED argument." "Return non-nil if DELAYED has been evaluated." (funcall delayed t)) +(defmacro thunk-let (bindings &rest body) + "Like `let' but create lazy bindings. + +BINDINGS is a list of elements of the form (SYMBOL EXPRESSION). +Any binding EXPRESSION is not evaluated before the variable +SYMBOL is used for the first time when evaluating the BODY. + +It is not allowed to set `thunk-let' or `thunk-let*' bound +variables. + +Using `thunk-let' and `thunk-let*' requires `lexical-binding'." + (declare (indent 1) (debug let)) + (cl-callf2 mapcar + (lambda (binding) + (pcase binding + (`(,(pred symbolp) ,_) binding) + (_ (signal 'error (cons "Bad binding in thunk-let" + (list binding)))))) + bindings) + (cl-callf2 mapcar + (pcase-lambda (`(,var ,binding)) + (list (make-symbol (concat (symbol-name var) "-thunk")) + var binding)) + bindings) + `(let ,(mapcar + (pcase-lambda (`(,thunk-var ,_var ,binding)) + `(,thunk-var (thunk-delay ,binding))) + bindings) + (cl-symbol-macrolet + ,(mapcar (pcase-lambda (`(,thunk-var ,var ,_binding)) + `(,var (thunk-force ,thunk-var))) + bindings) + ,@body))) + +(defmacro thunk-let* (bindings &rest body) + "Like `let*' but create lazy bindings. + +BINDINGS is a list of elements of the form (SYMBOL EXPRESSION). +Any binding EXPRESSION is not evaluated before the variable +SYMBOL is used for the first time when evaluating the BODY. + +It is not allowed to set `thunk-let' or `thunk-let*' bound +variables. + +Using `thunk-let' and `thunk-let*' requires `lexical-binding'." + (declare (indent 1) (debug let)) + (cl-reduce + (lambda (expr binding) `(thunk-let (,binding) ,expr)) + (nreverse bindings) + :initial-value (macroexp-progn body))) + +;; (defalias 'lazy-let #'thunk-let) +;; (defalias 'lazy-let* #'thunk-let*) + + (provide 'thunk) ;;; thunk.el ends here diff --git a/test/lisp/emacs-lisp/thunk-tests.el b/test/lisp/emacs-lisp/thunk-tests.el index 973a14b818..a63ce289e8 100644 --- a/test/lisp/emacs-lisp/thunk-tests.el +++ b/test/lisp/emacs-lisp/thunk-tests.el @@ -51,5 +51,55 @@ (thunk-force thunk) (should (= x 1)))) + + +;; thunk-let tests + +(ert-deftest thunk-let-basic-test () + "Test whether bindings are established." + (should (equal (thunk-let ((x 1) (y 2)) (+ x y)) 3))) + +(ert-deftest thunk-let*-basic-test () + "Test whether bindings are established." + (should (equal (thunk-let* ((x 1) (y (+ 1 x))) (+ x y)) 3))) + +(ert-deftest thunk-let-bound-vars-cant-be-set-test () + "Test whether setting a `thunk-let' bound variable fails." + (should-error + (eval '(thunk-let ((x 1)) (let ((y 7)) (setq x (+ x y)) (* 10 x))) t))) + +(ert-deftest thunk-let-laziness-test () + "Test laziness of `thunk-let'." + (should + (equal (let ((x-evalled nil) + (y-evalled nil)) + (thunk-let ((x (progn (setq x-evalled t) (+ 1 2))) + (y (progn (setq y-evalled t) (+ 3 4)))) + (let ((evalled-y y)) + (list x-evalled y-evalled evalled-y)))) + (list nil t 7)))) + +(ert-deftest thunk-let*-laziness-test () + "Test laziness of `thunk-let*'." + (should + (equal (let ((x-evalled nil) + (y-evalled nil) + (z-evalled nil) + (a-evalled nil)) + (thunk-let* ((x (progn (setq x-evalled t) (+ 1 1))) + (y (progn (setq y-evalled t) (+ x 1))) + (z (progn (setq z-evalled t) (+ y 1))) + (a (progn (setq a-evalled t) (+ z 1)))) + (let ((evalled-z z)) + (list x-evalled y-evalled z-evalled a-evalled evalled-z)))) + (list t t t nil 4)))) + +(ert-deftest thunk-let-bad-binding-test () + "Test whether a bad binding causes an error when expanding." + (should-error (macroexpand '(thunk-let ((x 1 1)) x))) + (should-error (macroexpand '(thunk-let (27) x))) + (should-error (macroexpand '(thunk-let x x)))) + + (provide 'thunk-tests) ;;; thunk-tests.el ends here commit ef183144add2b92359a9ade2ec0b28681b26956b Author: Phillip Lord Date: Wed Nov 29 21:28:44 2017 +0000 Add date to dependency and source zips for snapshots * admin/nt/dist-build/build-zips.sh, admin/nt/dist-build/build-dep-zips.py: Support snapshot naming diff --git a/admin/nt/dist-build/build-dep-zips.py b/admin/nt/dist-build/build-dep-zips.py index f71988692c..6ec8fafaf8 100755 --- a/admin/nt/dist-build/build-dep-zips.py +++ b/admin/nt/dist-build/build-dep-zips.py @@ -103,8 +103,8 @@ def gather_deps(deps, arch, directory): ## And package them up os.chdir(directory) print("Zipping: {}".format(arch)) - check_output_maybe("zip -9r ../../emacs-{}-{}-deps.zip *" - .format(EMACS_MAJOR_VERSION, arch), + check_output_maybe("zip -9r ../../emacs-{}-{}{}-deps.zip *" + .format(EMACS_MAJOR_VERSION, DATE, arch), shell=True) os.chdir("../../") @@ -168,8 +168,8 @@ def gather_source(deps): p.map(download_source,to_download) print("Zipping") - check_output_maybe("zip -9 ../emacs-{}-deps-mingw-w64-src.zip *" - .format(EMACS_MAJOR_VERSION), + check_output_maybe("zip -9 ../emacs-{}-{}deps-mingw-w64-src.zip *" + .format(EMACS_MAJOR_VERSION,DATE), shell=True) os.chdir("..") @@ -189,13 +189,16 @@ def clean(): parser = argparse.ArgumentParser() +parser.add_argument("-s", help="snapshot build", + action="store_true") + parser.add_argument("-t", help="32 bit deps only", action="store_true") parser.add_argument("-f", help="64 bit deps only", action="store_true") -parser.add_argument("-s", help="source code only", +parser.add_argument("-r", help="source code only", action="store_true") parser.add_argument("-c", help="clean only", @@ -205,19 +208,24 @@ def clean(): action="store_true") args = parser.parse_args() -do_all=not (args.c or args.s or args.f or args.t) +do_all=not (args.c or args.r or args.f or args.t) deps=extract_deps() DRY_RUN=args.d +if args.s: + DATE="{}-".format(check_output(["date", "+%Y-%m-%d"]).decode("utf-8").strip()) +else: + DATE="" + if( do_all or args.t ): gather_deps(deps,"i686","mingw32") if( do_all or args.f ): gather_deps(deps,"x86_64","mingw64") -if( do_all or args.s ): +if( do_all or args.r ): gather_source(deps) if( args.c ): diff --git a/admin/nt/dist-build/build-zips.sh b/admin/nt/dist-build/build-zips.sh index 2c7b56866f..5822d821a1 100755 --- a/admin/nt/dist-build/build-zips.sh +++ b/admin/nt/dist-build/build-zips.sh @@ -19,7 +19,7 @@ function git_up { - echo Making git worktree for Emacs $VERSION + echo [build] Making git worktree for Emacs $VERSION cd $HOME/emacs-build/git/emacs-$MAJOR_VERSION git pull git worktree add ../$BRANCH $BRANCH @@ -34,7 +34,7 @@ function build_zip { PKG=$2 HOST=$3 - echo Building Emacs-$VERSION for $ARCH + echo [build] Building Emacs-$VERSION for $ARCH if [ $ARCH == "i686" ] then PATH=/mingw32/bin:$PATH @@ -52,11 +52,12 @@ function build_zip { ## time that is not always needed if (($CONFIG)) then - ../../../git/$BRANCH/configure \ - --without-dbus \ - --host=$HOST --without-compress-install \ - $CACHE \ - CFLAGS="-O2 -static -g3" + echo [build] Configuring Emacs $ARCH + ../../../git/$BRANCH/configure \ + --without-dbus \ + --host=$HOST --without-compress-install \ + $CACHE \ + CFLAGS="-O2 -static -g3" fi make -j 16 install \ @@ -66,7 +67,18 @@ function build_zip { zip -r -9 emacs-$OF_VERSION-$ARCH-no-deps.zip * mv emacs-$OF_VERSION-$ARCH-no-deps.zip $HOME/emacs-upload rm bin/libXpm-noX4.dll - unzip $HOME/emacs-build/deps/emacs-$MAJOR_VERSION-$ARCH-deps.zip + + if [ -z $SNAPSHOT ]; + then + DEPS_FILE=$HOME/emacs-build/deps/emacs-$MAJOR_VERSION-$ARCH-deps.zip + else + ## Pick the most recent snapshot whatever that is + DEPS_FILE=`ls $HOME/emacs-build/deps/emacs-$MAJOR_VERSION-*-$ARCH-deps.zip | tail -n 1` + fi + + echo [build] Using $DEPS_FILE + unzip $DEPS_FILE + zip -r -9 emacs-$OF_VERSION-$ARCH.zip * mv emacs-$OF_VERSION-$ARCH.zip ~/emacs-upload } @@ -74,7 +86,7 @@ function build_zip { function build_installer { ARCH=$1 cd $HOME/emacs-build/install/emacs-$VERSION - echo Calling makensis in `pwd` + echo [build] Calling makensis in `pwd` cp ../../git/$BRANCH/admin/nt/dist-build/emacs.nsi . makensis -v4 \ @@ -148,14 +160,19 @@ fi if [ -z $VERSION ]; then - echo Cannot determine Emacs version + echo [build] Cannot determine Emacs version exit 1 fi MAJOR_VERSION="$(echo $VERSION | cut -d'.' -f1)" +## ACTUAL VERSION is the version declared by emacs ACTUAL_VERSION=$VERSION + +## VERSION includes the word snapshot if necessary VERSION=$VERSION$SNAPSHOT + +## OF version includes the date if we have a snapshot OF_VERSION=$VERSION if [ -z $SNAPSHOT ]; commit 3f3d98ee5851840228786390ee7dbf851d144eb8 Author: Glenn Morris Date: Thu Nov 30 12:40:46 2017 -0500 Make truncate-lines permanently local (bug#15396) Width of lines relative to display is rarely a function of major mode. * src/buffer.c (init_buffer_once) : Flag as permanently local. * lisp/bindings.el (truncate-lines): Add permanent-local property. diff --git a/lisp/bindings.el b/lisp/bindings.el index 2b664044de..2bad90351c 100644 --- a/lisp/bindings.el +++ b/lisp/bindings.el @@ -699,7 +699,7 @@ okay. See `mode-line-format'.") buffer-file-format buffer-auto-save-file-format buffer-display-count buffer-display-time enable-multibyte-characters - buffer-file-coding-system)) + buffer-file-coding-system truncate-lines)) ;; We have base64, md5 and sha1 functions built in now. (provide 'base64) diff --git a/src/buffer.c b/src/buffer.c index c6f9eb28e2..12a467daae 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -5134,7 +5134,9 @@ init_buffer_once (void) XSETFASTINT (BVAR (&buffer_local_flags, selective_display), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, selective_display_ellipses), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, tab_width), idx); ++idx; - XSETFASTINT (BVAR (&buffer_local_flags, truncate_lines), idx); ++idx; + XSETFASTINT (BVAR (&buffer_local_flags, truncate_lines), idx); + /* Make this one a permanent local. */ + buffer_permanent_local_flags[idx++] = 1; XSETFASTINT (BVAR (&buffer_local_flags, word_wrap), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, ctl_arrow), idx); ++idx; XSETFASTINT (BVAR (&buffer_local_flags, fill_column), idx); ++idx;