diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/mnesia/src/mnesia.erl | 8 | ||||
-rw-r--r-- | lib/mnesia/test/mnesia_trans_access_test.erl | 10 | ||||
-rw-r--r-- | lib/parsetools/include/leexinc.hrl | 35 | ||||
-rw-r--r-- | lib/parsetools/test/leex_SUITE.erl | 44 | ||||
-rw-r--r-- | lib/parsetools/test/yecc_SUITE.erl | 2 | ||||
-rw-r--r-- | lib/ssh/doc/src/notes.xml | 18 | ||||
-rw-r--r-- | lib/ssh/src/ssh.appup.src | 4 | ||||
-rw-r--r-- | lib/ssh/src/ssh_channel.erl | 7 | ||||
-rw-r--r-- | lib/tools/emacs/Makefile | 1 | ||||
-rw-r--r-- | lib/tools/emacs/erlang-edoc.el | 172 | ||||
-rw-r--r-- | lib/tools/emacs/erlang-start.el | 1 | ||||
-rw-r--r-- | lib/tools/emacs/erlang.el | 5 |
12 files changed, 290 insertions, 17 deletions
diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl index 9586adbf93..5bf2fc2dc3 100644 --- a/lib/mnesia/src/mnesia.erl +++ b/lib/mnesia/src/mnesia.erl @@ -1409,8 +1409,14 @@ select_cont(Tid,_,State=#mnesia_select{tid=Tid,written=[]}) -> select_state(dirty_sel_cont(State),State); select_cont(Tid,_Ts,State=#mnesia_select{tid=Tid}) -> trans_select(dirty_sel_cont(State), State); -select_cont(_Tid2,_,#mnesia_select{tid=_Tid1}) -> % Missmatching tids +select_cont(Tid2,_,#mnesia_select{tid=_Tid1}) + when element(1,Tid2) == tid -> % Mismatching tids abort(wrong_transaction); +select_cont(Tid,Ts,State=#mnesia_select{}) -> + % Repair mismatching tids in non-transactional contexts + RepairedState = State#mnesia_select{tid = Tid, written = [], + spec = undefined, type = undefined}, + select_cont(Tid,Ts,RepairedState); select_cont(_,_,Cont) -> abort({badarg, Cont}). diff --git a/lib/mnesia/test/mnesia_trans_access_test.erl b/lib/mnesia/test/mnesia_trans_access_test.erl index aa50ee4cb1..4ed73ea859 100644 --- a/lib/mnesia/test/mnesia_trans_access_test.erl +++ b/lib/mnesia/test/mnesia_trans_access_test.erl @@ -307,6 +307,7 @@ select14(Config) when is_list(Config) -> %% Some Helpers Trans = fun(Fun) -> mnesia:transaction(Fun) end, + Dirty = fun(Fun) -> mnesia:async_dirty(Fun) end, LoopHelp = fun('$end_of_table',_) -> []; ({Recs,Cont},Fun) -> Sel = mnesia:select(Cont), @@ -334,8 +335,13 @@ select14(Config) when is_list(Config) -> ?match({atomic, [OneRec]}, Trans(fun() -> Loop(Tab, OnePat) end)), ?match({atomic, All}, Trans(fun() -> Loop(Tab, AllPat) end)), - {atomic,{_, Cont}} = Trans(fun() -> mnesia:select(Tab, OnePat, 1, read) end), - ?match({aborted, wrong_transaction}, Trans(fun() -> mnesia:select(Cont) end)), + {atomic,{_, ContOne}} = Trans(fun() -> mnesia:select(Tab, OnePat, 1, read) end), + ?match({aborted, wrong_transaction}, Trans(fun() -> mnesia:select(ContOne) end)), + ?match('$end_of_table', Dirty(fun() -> mnesia:select(ContOne) end)), + + {atomic,{_, ContAll}} = Trans(fun() -> mnesia:select(Tab, AllPat, 1, read) end), + ?match({aborted, wrong_transaction}, Trans(fun() -> mnesia:select(ContAll) end)), + ?match({[_], _}, Dirty(fun() -> mnesia:select(ContAll) end)), ?match({aborted, _}, Trans(fun() -> mnesia:select(Tab, {match, '$1', 2},1,read) end)), ?match({aborted, _}, Trans(fun() -> mnesia:select(Tab, [{'_', [], '$1'}],1,read) end)), diff --git a/lib/parsetools/include/leexinc.hrl b/lib/parsetools/include/leexinc.hrl index 2657fdcfaa..b4449607cb 100644 --- a/lib/parsetools/include/leexinc.hrl +++ b/lib/parsetools/include/leexinc.hrl @@ -36,8 +36,10 @@ string(Ics0, L0, Tcs, Ts) -> string_cont(Ics1, L1, yyaction(A, Alen, Tcs, L0), Ts); {reject,_Alen,Tlen,_Ics1,L1,_S1} -> % After a non-accepting state {error,{L0,?MODULE,{illegal,yypre(Tcs, Tlen+1)}},L1}; - {A,Alen,_Tlen,_Ics1,_L1,_S1} -> - string_cont(yysuf(Tcs, Alen), L0, yyaction(A, Alen, Tcs, L0), Ts) + {A,Alen,Tlen,_Ics1,L1,_S1} -> + Tcs1 = yysuf(Tcs, Alen), + L2 = adjust_line(Tlen, Alen, Tcs1, L1), + string_cont(Tcs1, L2, yyaction(A, Alen, Tcs, L0), Ts) end. %% string_cont(RestChars, Line, Token, Tokens) @@ -107,8 +109,10 @@ token(S0, Ics0, L0, Tcs, Tlen0, Tline, A0, Alen0) -> {reject,_Alen1,Tlen1,Ics1,L1,_S1} -> % No token match Error = {Tline,?MODULE,{illegal,yypre(Tcs, Tlen1+1)}}, {done,{error,Error,L1},Ics1}; - {A1,Alen1,_Tlen1,_Ics1,_L1,_S1} -> % Use last accept match - token_cont(yysuf(Tcs, Alen1), L0, yyaction(A1, Alen1, Tcs, Tline)) + {A1,Alen1,Tlen1,_Ics1,L1,_S1} -> % Use last accept match + Tcs1 = yysuf(Tcs, Alen1), + L2 = adjust_line(Tlen1, Alen1, Tcs1, L1), + token_cont(Tcs1, L2, yyaction(A1, Alen1, Tcs, Tline)) end. %% token_cont(RestChars, Line, Token) @@ -181,9 +185,11 @@ tokens(S0, Ics0, L0, Tcs, Tlen0, Tline, Ts, A0, Alen0) -> %% Skip rest of tokens. Error = {L1,?MODULE,{illegal,yypre(Tcs, Tlen1+1)}}, skip_tokens(yysuf(Tcs, Tlen1+1), L1, Error); - {A1,Alen1,_Tlen1,_Ics1,_L1,_S1} -> + {A1,Alen1,Tlen1,_Ics1,L1,_S1} -> Token = yyaction(A1, Alen1, Tcs, Tline), - tokens_cont(yysuf(Tcs, Alen1), L0, Token, Ts) + Tcs1 = yysuf(Tcs, Alen1), + L2 = adjust_line(Tlen1, Alen1, Tcs1, L1), + tokens_cont(Tcs1, L2, Token, Ts) end. %% tokens_cont(RestChars, Line, Token, Tokens) @@ -235,9 +241,11 @@ skip_tokens(S0, Ics0, L0, Tcs, Tlen0, Tline, Error, A0, Alen0) -> {done,{error,Error,L1},eof}; {reject,_Alen1,Tlen1,_Ics1,L1,_S1} -> skip_tokens(yysuf(Tcs, Tlen1+1), L1, Error); - {A1,Alen1,_Tlen1,_Ics1,L1,_S1} -> + {A1,Alen1,Tlen1,_Ics1,L1,_S1} -> Token = yyaction(A1, Alen1, Tcs, Tline), - skip_cont(yysuf(Tcs, Alen1), L1, Token, Error) + Tcs1 = yysuf(Tcs, Alen1), + L2 = adjust_line(Tlen1, Alen1, Tcs1, L1), + skip_cont(Tcs1, L2, Token, Error) end. %% skip_cont(RestChars, Line, Token, Error) @@ -269,6 +277,17 @@ yyrev(List, Tail) -> lists:reverse(List, Tail). yypre(List, N) -> lists:sublist(List, N). yysuf(List, N) -> lists:nthtail(N, List). +%% adjust_line(TokenLength, AcceptLength, Chars, Line) -> NewLine +%% Make sure that newlines in Chars are not counted twice. +%% Line has been updated with respect to newlines in the prefix of +%% Chars consisting of (TokenLength - AcceptLength) characters. + +adjust_line(N, N, _Cs, L) -> L; +adjust_line(T, A, [$\n|Cs], L) -> + adjust_line(T-1, A, Cs, L-1); +adjust_line(T, A, [_|Cs], L) -> + adjust_line(T-1, A, Cs, L). + %% yystate() -> InitialState. %% yystate(State, InChars, Line, CurrTokLen, AcceptAction, AcceptLen) -> %% {Action, AcceptLen, RestChars, Line} | diff --git a/lib/parsetools/test/leex_SUITE.erl b/lib/parsetools/test/leex_SUITE.erl index 84f9c996ac..54602848ec 100644 --- a/lib/parsetools/test/leex_SUITE.erl +++ b/lib/parsetools/test/leex_SUITE.erl @@ -45,7 +45,7 @@ pt/1, man/1, ex/1, ex2/1, not_yet/1, line_wrap/1, - otp_10302/1, otp_11286/1, unicode/1]). + otp_10302/1, otp_11286/1, unicode/1, otp_13916/1]). % Default timetrap timeout (set in init_per_testcase). -define(default_timeout, ?t:minutes(1)). @@ -62,12 +62,12 @@ end_per_testcase(_Case, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [{group, checks}, {group, examples}, {group, bugs}]. + [{group, checks}, {group, examples}, {group, tickets}, {group, bugs}]. groups() -> [{checks, [], [file, compile, syntax]}, {examples, [], [pt, man, ex, ex2, not_yet, unicode]}, - {tickets, [], [otp_10302, otp_11286]}, + {tickets, [], [otp_10302, otp_11286, otp_13916]}, {bugs, [], [line_wrap]}]. init_per_suite(Config) -> @@ -1052,7 +1052,7 @@ otp_11286(Config) when is_list(Config) -> Dir = ?privdir, UName = [1024] ++ "u", UDir = filename:join(Dir, UName), - ok = rpc:call(Node, file, make_dir, [UDir]), + _ = rpc:call(Node, file, make_dir, [UDir]), %% Note: Cannot use UName as filename since the filename is used %% as module name. To be fixed in R18. @@ -1095,6 +1095,42 @@ otp_11286(Config) when is_list(Config) -> true = test_server:stop_node(Node), ok. +otp_13916(doc) -> + "OTP-13916. Leex rules with newlines result in bad line numbers"; +otp_13916(suite) -> []; +otp_13916(Config) when is_list(Config) -> + Ts = [{otp_13916_1, + <<"Definitions.\n" + "W = [a-zA-Z0-9]\n" + "S = [\\s\\t]\n" + "B = [\\n\\r]\n" + "Rules.\n" + "%% mark line break(s) and empty lines by token 'break'\n" + "%% in order to use as delimiters\n" + "{B}({S}*{B})+ : {token, {break, TokenLine}}.\n" + "{B} : {token, {break, TokenLine}}.\n" + "{S}+ : {token, {blank, TokenLine, TokenChars}}.\n" + "{W}+ : {token, {word, TokenLine, TokenChars}}.\n" + "Erlang code.\n" + "-export([t/0]).\n" + "t() ->\n" + " {ok,[{break,1},{blank,4,\" \"},{word,4,\"breaks\"}],4} =\n" + " string(\"\\n\\n \\n breaks\"),\n" + " {ok,[{break,1},{word,4,\"works\"}],4} =\n" + " string(\"\\n\\n \\nworks\"),\n" + " {ok,[{break,1},{word,4,\"L4\"},{break,4},\n" + " {word,5,\"L5\"},{break,5},{word,7,\"L7\"}], 7} =\n" + " string(\"\\n\\n \\nL4\\nL5\\n\\nL7\"),\n" + " {ok,[{break,1},{blank,4,\" \"},{word,4,\"L4\"},\n" + " {break,4},{blank,5,\" \"},{word,5,\"L5\"},\n" + " {break,5},{blank,7,\" \"},{word,7,\"L7\"}], 7} =\n" + " string(\"\\n\\n \\n L4\\n L5\\n\\n L7\"),\n" + " ok.\n">>, + default, + ok}], + ?line run(Config, Ts), + ok. + start_node(Name, Args) -> [_,Host] = string:tokens(atom_to_list(node()), "@"), ct:log("Trying to start ~w@~s~n", [Name,Host]), diff --git a/lib/parsetools/test/yecc_SUITE.erl b/lib/parsetools/test/yecc_SUITE.erl index e91ddb11d1..5bd71d5d19 100644 --- a/lib/parsetools/test/yecc_SUITE.erl +++ b/lib/parsetools/test/yecc_SUITE.erl @@ -2009,7 +2009,7 @@ otp_11286(Config) when is_list(Config) -> Dir = ?privdir, UName = [1024] ++ "u", UDir = filename:join(Dir, UName), - ok = rpc:call(Node, file, make_dir, [UDir]), + _ = rpc:call(Node, file, make_dir, [UDir]), %% Note: Cannot use UName as filename since the filename is used %% as module name. To be fixed in R18. diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index b990c18e9a..a4897668e4 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -30,6 +30,24 @@ <file>notes.xml</file> </header> +<section><title>Ssh 4.3.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Handle all possible exit values that should be + interpreted as {error, closed}. Failing to do so could + lead to unexpected crashes for users of the ssh + application.</p> + <p> + Own Id: OTP-13932 Aux Id: seq13189 </p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 4.3.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src index e38cecf226..4cda8fee95 100644 --- a/lib/ssh/src/ssh.appup.src +++ b/lib/ssh/src/ssh.appup.src @@ -20,9 +20,13 @@ {"%VSN%", [ + {<<"4.3.2">>, [{load_module, ssh_channel, soft_purge, soft_purge, []} + ]}, {<<".*">>, [{restart_application, ssh}]} ], [ + {<<"4.3.2">>, [{load_module, ssh_channel, soft_purge, soft_purge, []} + ]}, {<<".*">>, [{restart_application, ssh}]} ] }. diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl index a8e6ebde16..426e2f5125 100644 --- a/lib/ssh/src/ssh_channel.erl +++ b/lib/ssh/src/ssh_channel.erl @@ -93,11 +93,16 @@ call(ChannelPid, Msg, TimeOute) -> catch exit:{noproc, _} -> {error, closed}; + exit:{normal, _} -> + {error, closed}; + exit:{shutdown, _} -> + {error, closed}; + exit:{{shutdown, _}, _} -> + {error, closed}; exit:{timeout, _} -> {error, timeout} end. - cast(ChannelPid, Msg) -> gen_server:cast(ChannelPid, Msg). diff --git a/lib/tools/emacs/Makefile b/lib/tools/emacs/Makefile index 585425e5f1..e1b195ef97 100644 --- a/lib/tools/emacs/Makefile +++ b/lib/tools/emacs/Makefile @@ -43,6 +43,7 @@ EMACS_FILES= \ erlang_appwiz \ erlang-start \ erlang-eunit \ + erlang-edoc \ erlang-flymake \ erlang diff --git a/lib/tools/emacs/erlang-edoc.el b/lib/tools/emacs/erlang-edoc.el new file mode 100644 index 0000000000..034036ad04 --- /dev/null +++ b/lib/tools/emacs/erlang-edoc.el @@ -0,0 +1,172 @@ +;;; erlang-edoc.el --- EDoc support for Erlang mode -*- lexical-binding: t; -*- + +;; %CopyrightBegin% +;; +;; Copyright Ericsson AB 1996-2016. All Rights Reserved. +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. +;; +;; %CopyrightEnd% + +;;; Commentary: + +;; Ref: http://www.erlang.org/doc/apps/edoc/users_guide.html +;; +;; To use: (add-hook 'erlang-mode-hook 'erlang-edoc-mode) + +;;; Code: + +(defcustom erlang-edoc-indent-level 2 + "Indentation level of xhtml in Erlang edoc." + :safe 'integerp + :group 'erlang) + +(defvar erlang-edoc-generic-tags + '("clear" "docfile" "end" "headerfile" "todo" "TODO" "type") + "Tags that can be used anywhere within a module.") + +(defvar erlang-edoc-overview-tags + '("author" "copyright" "reference" "see" "since" "title" "version") + "Tags that can be used in an overview file.") + +(defvar erlang-edoc-module-tags + '("author" "copyright" "deprecated" "doc" "hidden" "private" "reference" + "see" "since" "version") + "Tags that can be used before a module declaration.") + +(defvar erlang-edoc-function-tags + '("deprecated" "doc" "equiv" "hidden" "private" "see" "since" "spec" + "throws" "type") + "Tags that can be used before a function definition.") + +(defvar erlang-edoc-predefined-macros + '("date" "docRoot" "link" "module" "package" "section" "time" + "type" "version")) + +(defface erlang-edoc-tag '((t (:inherit font-lock-constant-face))) + "Face used to highlight edoc tags." + :group 'erlang) + +(defface erlang-edoc-macro '((t (:inherit font-lock-preprocessor-face))) + "Face used to highlight edoc macros." + :group 'erlang) + +(defface erlang-edoc-verbatim + '((t (:family "Monospace" :inherit font-lock-keyword-face))) + "Face used to highlight verbatim text." + :group 'erlang) + +(defface erlang-edoc-todo '((t (:inherit font-lock-warning-face))) + "Face used to highlight edoc macros." + :group 'erlang) + +(defface erlang-edoc-heading '((t (:inherit bold))) + "Face used to highlight edoc headings." + :group 'erlang) + +(defvar erlang-edoc-font-lock-keywords + '(("^%+\\s-*\\(@\\w+\\)\\_>" 1 'erlang-edoc-tag prepend) + ("^%+\\s-*" ("{\\(@\\w+\\)\\_>" nil nil (1 'erlang-edoc-macro prepend))) + ("^%+\\s-*" ("\\(?:@@\\)*\\(@[@{}]\\)" nil nil (1 'escape-glyph prepend))) + ("^%+\\s-*@\\(deprecated\\)\\_>" 1 font-lock-warning-face prepend) + ;; http://www.erlang.org/doc/apps/edoc/chapter.html#Wiki_notation + ("^%+\\s-*" ("[^`]`\\([^`]?\\|[^`].*?[^']\\)'" + (forward-char -1) nil (1 'erlang-edoc-verbatim prepend))) + ("^%+\\s-*" ("\\[\\(\\(?:https?\\|file\\|ftp\\)://[^][]+\\)\\]" + nil nil (1 'link prepend))) + ("^%+\\s-*\\(?:\\(?1:@todo\\|@TODO\\)\\_>\\|\\(?1:TODO\\):\\)" + 1 'erlang-edoc-todo prepend) + ("^%+\\s-*\\(\\(=\\{2,4\\}\\)[^=\n].*[^=\n]\\2\\)\\s-*$" + 1 'erlang-edoc-heading prepend))) + +(defun erlang-edoc-xml-context () + "Parse edoc x(ht)ml context at comment start of current line." + (eval-and-compile (require 'xmltok)) + (save-excursion + (beginning-of-line) + (when (looking-at "^%+\\s-*") + (let ((pt (match-end 0)) context) + (forward-comment (- (point))) + (while (< (point) pt) + (xmltok-forward) + (cond ((eq xmltok-type 'start-tag) + (push (cons xmltok-type xmltok-start) context)) + ((eq xmltok-type 'end-tag) + (pop context)))) + (goto-char pt) + (xmltok-forward) + (push (car (memq xmltok-type '(start-tag end-tag))) context) + context)))) + +(defun erlang-edoc-indent-line () + (let ((context (erlang-edoc-xml-context))) + (when context + (save-excursion + (beginning-of-line) + (re-search-forward "^%+\\s-*" (line-end-position)) + (when (or (car context) (cadr context)) + (let ((pad (when (cadr context) + (save-excursion + (goto-char (cdr (cadr context))) + (- (current-column) + (progn + (beginning-of-line) + (skip-chars-forward "%") + (current-column))))))) + (just-one-space (cond ((not pad) 1) + ((eq (car context) 'end-tag) pad) + (t (+ erlang-edoc-indent-level pad))))))) + (when (looking-back "^%*\\s-*" (line-beginning-position)) + (re-search-forward "\\=%*\\s-*"))))) + +(defun erlang-edoc-before-module-declaration-p () + (save-excursion + (beginning-of-line) + (forward-comment (point-max)) + (or (eobp) (re-search-forward "^-module\\s-*(" nil t)))) + +(defun erlang-edoc-completion-at-point () + (when (eq (syntax-ppss-context (syntax-ppss)) 'comment) + (save-excursion + (skip-syntax-backward "w_") + (when (= (preceding-char) ?@) + (let* ((is-tag (looking-back "^%+\\s-*@" (line-beginning-position))) + (beg (point)) + (end (progn (skip-syntax-forward "w_") (point))) + (table (cond + ((not is-tag) + erlang-edoc-predefined-macros) + ((erlang-edoc-before-module-declaration-p) + (append erlang-edoc-module-tags + erlang-edoc-generic-tags)) + (t (append erlang-edoc-function-tags + erlang-edoc-generic-tags))))) + (list beg end table)))))) + +;;;###autoload +(define-minor-mode erlang-edoc-mode nil + :lighter " EDoc" + (cond (erlang-edoc-mode + (add-hook 'erlang-indent-line-hook #'erlang-edoc-indent-line nil t) + (font-lock-add-keywords nil erlang-edoc-font-lock-keywords t) + (add-hook 'completion-at-point-functions + #'erlang-edoc-completion-at-point nil t)) + (t + (remove-hook 'erlang-indent-line-hook #'erlang-edoc-indent-line t) + (font-lock-remove-keywords nil erlang-edoc-font-lock-keywords) + (remove-hook 'completion-at-point-functions + #'erlang-edoc-completion-at-point t))) + (jit-lock-refontify)) + +(provide 'erlang-edoc) +;;; erlang-edoc.el ends here diff --git a/lib/tools/emacs/erlang-start.el b/lib/tools/emacs/erlang-start.el index 76e0575e68..f9a6d24b2c 100644 --- a/lib/tools/emacs/erlang-start.el +++ b/lib/tools/emacs/erlang-start.el @@ -78,6 +78,7 @@ (autoload 'erlang-find-tag-other-window "erlang" "Like `find-tag-other-window'. Capable of retreiving Erlang modules.") +(autoload 'erlang-edoc-mode "erlang-edoc" "Toggle Erlang-Edoc mode on or off." t) ;; ;; Associate files extensions ".erl" and ".hrl" with Erlang mode. diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el index 788de75b3a..67e88bac30 100644 --- a/lib/tools/emacs/erlang.el +++ b/lib/tools/emacs/erlang.el @@ -78,6 +78,10 @@ ;; Variables: +(defgroup erlang nil + "The Erlang programming language." + :group 'languages) + (defconst erlang-version "2.7" "The version number of Erlang mode.") @@ -2444,6 +2448,7 @@ Return the amount the indentation changed by." ;; after the indentation. Else stay at same point in text. (if (> (- (point-max) pos) (point)) (goto-char (- (point-max) pos))) + (run-hooks 'erlang-indent-line-hook) shift-amt)) |